AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 792673
Accepted
hollowillow
hollowillow
Asked: 2025-03-19 06:09:04 +0800 CST2025-03-19 06:09:04 +0800 CST 2025-03-19 06:09:04 +0800 CST

Precisa de ajuda com script de shell - processando a mesma opção várias vezes

  • 772

Então eu me deparei com um pouco de barreira, eu tenho uma opção no meu script que chama uma função que me permite especificar um arquivo/diretório e então eu quero analisar essa saída em uma ferramenta de menu (usando dmenu neste caso) para selecionar qual arquivo é o que eu quero especificamente e continuar trabalhando com essa seleção como uma variável no mesmo script. Isso funciona bem se for apenas um arquivo ou diretório, mas eu quero ser capaz de usar a opção várias vezes e então analisar toda essa saída de uma vez para dmenu. Aqui está um trecho

fileSelection () {
    if [ -d "${OPTARG}" ]; then find "${OPTARG}" -type f; fi;
    if [ -f "${OPTARG}" ]; then printf '%s\n' "${OPTARG}"; fi;
}

while getopts "f:" option; do
   case "${option}" in
       f) file="$(fileSelection|dmenu)";;
   esac
done

E como eu disse, isso funciona se eu fizer:

myscript -f file

ou

myscript -f directory

mas eu esperava poder fazer isso também:

myscript -f file1 -f file2

O problema é que, como a função é chamada consecutivamente, não consigo analisar a saída no dmenu dessa forma, porque ele não invoca o dmenu com as opções file1 e file2, mas primeiro com file1 e depois com file2. Espero que isso faça sentido.

Pode haver alguma solução realmente simples que eu esteja esquecendo, pensei em simplesmente escrever a saída em um arquivo e então analisar isso, o que pode funcionar, mas gostaria de evitar o pipe para arquivos, se possível. Também estou tentando mantê-lo compatível com POSIX, e apreciaria respostas que sigam isso.

shell-script
  • 4 4 respostas
  • 106 Views

4 respostas

  • Voted
  1. Best Answer
    Chris Davies
    2025-03-19T07:08:02+08:002025-03-19T07:08:02+08:00

    Você pode coletar os valores derivados de seu -fe passá-los todos juntos para dmenu. Aqui está um exemplo que funcionará para a maioria dos diretórios e arquivos - mas não aqueles que contêm uma nova linha incorporada:

    #!/bin/bash
    #
    if ! type dmenu >/dev/null 2>&1
    then
        # Demonstration code in case dmenu is not installed
        dmenu() {
            echo "This is a fake implementation of dmenu" >&2
            sed 's/^/>   /' >&2
        }
    fi
    
    
    # List of files from the -f option
    files=()
    
    # Loop across the command line
    while getopts "f:" OPT
    do
       case $OPT in
           f)
                # Disable globbing
                OIFS="$IFS" IFS=$'\n'
                noglob=$(set -o | awk '$1=="noglob"{print $2}')
                set -o noglob
    
                # Capture the specified set of files
                files+=( $(find "$OPTARG" -type f) )
    
                # Reenable globbing
                IFS="$OIFS"
                [ "$noglob" = 'off' ] && set +o noglob
                ;;
       esac
    done
    
    # If we have some files then pass them to dmenu
    [ "${#files[@]}" -gt 0 ] && printf '%s\n' "${files[@]}" | dmenu
    

    Já que você quer uma solução POSIX, e já está assumindo que nomes de arquivo não podem conter quebras de linha, você pode construir uma única sequência de valores terminados em quebra de linha e usá-la. Na verdade, neste caso eu provavelmente construiria o conjunto de pontos de partida e então usaria find | dmenuentre eles em vez de construir o conjunto de resultados como fiz na minha primeira abordagem.

    #!/bin/sh
    
    # List of source files from the -f option
    files=
    
    # Loop across the command line
    nl='
    '
    while getopts "f:" OPT
    do
       case $OPT in
           f)
                # Capture the source file name
                files="$files$OPTARG$nl"
                ;;
       esac
    done
    
    # If we have some files then pass them to dmenu
    if [ -n "$files" ]
    then
        while [ -n "$files" ]
        do
            file=${files%%$nl*}     # get first NL-terminated entry
            files=${files#*$nl}     # strip it off the list
            find "$file" -type f    # generate the set of files
        done |
            dmenu
    fi
    
    • 1
  2. Stéphane Chazelas
    2025-03-19T17:08:21+08:002025-03-19T17:08:21+08:00

    Você fileSelection|dmenupressupõe que os caminhos dos arquivos não contêm caracteres de nova linha, então você pode criar $fileuma lista separada/delimitada por nova linha:

    NL='
    '
    files=
    ...
      (f) files=$files$(fileSelection|dmenu)$NL;;
    ...
    
    # to use $files, like pass each file as separate arguments to a
    # command, use split+glob with glob disabled
    
    IFS=$NL; set -o noglob
    cmd -- $files
    

    Uma alternativa é gerar o set -- ...código shell para definir a lista de parâmetros posicionais para a lista de arquivos

    sh_quote() {
      LC_ALL=C sed "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"
    }
    
    files=
    ...
      (f) files="$files $(fileSelection|dmenu|sh_quote)";;
    ...
    
    # and to use either
    eval "cmd -- $files"
    
    # or
    eval "set -- $files"
    cmd -- "$@"
    

    POSIXly, isso ainda é perigoso, pois sednão é necessário para funcionar corretamente com linhas que excedam LINE_MAX bytes de comprimento e não há garantia de que a saída findtenha linhas tão curtas (nem mesmo menores que o próprio PATH_MAX, não há garantia de que seja menor que o LINE_MAX).

    • 0
  3. Kusalananda
    2025-03-19T19:08:02+08:002025-03-19T19:08:02+08:00

    Faça com que a opção de análise while-loop produza os nomes encontrados e passe a saída dele para dmenu. Ou seja, mova o piping para dmenuda saída de fileSelectionpara a saída do while-loop.

    Para fazer isso corretamente, primeiro precisamos ter certeza de que realmente há pelo menos uma -fopção na linha de comando. Isso significa ter que analisar as opções duas vezes: uma para manipular qualquer outra opção e detectar um -f, e mais uma vez para agir nas -fopções e obter a lista de arquivos e deixar o usuário escolher filename em nossa filevariável.

    #!/bin/sh
    
    usage () {
            echo 'use with "-h" or "-f somepath" (optionally repeated)'
    }
    
    fileSelection () {
        if [ -d "${OPTARG}" ]; then find "${OPTARG}" -type f; fi;
        if [ -f "${OPTARG}" ]; then printf '%s\n' "${OPTARG}"; fi;
    }
    
    # Parse command line options and set a flag
    # if we have any -f options to deal with.
    f_flag=false
    while getopts hf: opt; do
            case $opt in
                    h) usage; exit ;;
                    f) f_flag=true ;;
                    *) echo 'error' >&2; exit 1
            esac
    done
    
    if "$f_flag"; then
            # Now parse all options again,
            # but only care about the -f options.
            OPTIND=1
    
            file=$(
                    while getopts hf: opt; do
                            case $opt in (f) fileSelection; esac
                    done | dmenu
            )
    fi
    
    • 0
  4. hollowillow
    2025-03-19T16:40:50+08:002025-03-19T16:40:50+08:00

    Talvez eu tenha encontrado uma solução depois de analisar mais a fundo a maneira POSIX de fazer matrizes

    #!/bin/sh
    
    fileSelection () {
        printf '%s\n' "${OPTARG}"
    }
    
    while getopts "f:" option; do
    case "${option}" in
        f) set -- "$@" "$(fileSelection)";;
    esac
    done
    
    printf '%s\n' "$@" | sed "/^-/ d;"
    

    esta é uma versão muito simplificada do script, mas simplesmente analisar de sed para dmenu deve produzir o que eu quero. Vou verificar novamente quando estiver em casa. Obrigado a Chris Davies por me apontar a direção certa.

    • -1

relate perguntas

  • Subtraindo a mesma coluna entre duas linhas no awk

  • Um script que imprime as linhas de um arquivo com seu comprimento [fechado]

  • exportar variáveis ​​​​env programaticamente, via stdout do comando [duplicado]

  • Dividir por delimitador e concatenar problema de string

  • MySQL Select com função IN () com array bash

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve