echo \\ * - comportamento de escape de barra invertida bash, é avaliado de trás para frente?

5

Então, no bash, Quando eu faço

echo \*
*

Isso parece certo, já que * é escapado e tomado literalmente.

Mas não consigo entender isso quando eu faço

echo \*
\*

Eu achei que a primeira barra invertida escapou da segunda, assim duas barras invertidas "\\" me darão um "\" literalmente. e * seguiu carregando seu significado especial. Eu estava esperando:

echo \*
\file1 file2 file3
RESUMO DE RESPOSTA: Como \ é tomado literalmente, echo \* se comportará exatamente como echo a* , que encontrará qualquer arquivo que comece com literal "a".

Pergunta de acompanhamento, Se eu quiser imprimir exatamente como

\file1 file2 file3

Qual comando devo usar? por exemplo. como o seguinte, mas eu não quero espaço

echo \ * 
\ file1 file2 file3
    
por Weishi Zeng 11.03.2015 / 07:48

6 respostas

6

Se você não tiver um arquivo no diretório atual cujo nome começa com uma barra invertida, esse é o comportamento esperado. Bash expande * para corresponder a qualquer nome de arquivo existente, mas :

If the pattern does not match any existing filenames or pathnames, the pattern string shall be left unchanged.

Como não havia nome de arquivo começando com \ , o padrão foi deixado como está e echo recebe o argumento \* . Esse comportamento é geralmente confuso, e alguns outros shells, como zsh , não o possuem. Você pode alterá-lo no Bash usando shopt -o failglob , que será então dê um erro como zsh e ajude-o a diagnosticar o problema em vez de se comportar mal.

Os caracteres de padrão * e ? podem aparecer em qualquer parte da palavra e os caracteres antes e depois são correspondidos literalmente. É por isso que echo \* e echo \ * fornecem uma saída tão diferente: a primeira corresponde a qualquer coisa que inicie com \ (e falha) e a segunda gera um \ e, em seguida, todos os nomes de arquivos.

A maneira mais direta de obter a saída desejada com segurança é provavelmente usar printf :

printf '\'; printf "%s " *

echo * não é seguro no caso de nomes de arquivos incomuns com - ou \ em nenhum caso.

    
por 11.03.2015 / 08:09
1

Tente isto:

printf "\%s" "$(echo *)"

Explicação:

  • printf recebe um argumento de formato e zero ou mais argumentos que são substituídos na string de formato
  • \ no formato tem o mesmo significado que em echo \
  • %s significa pegar o próximo argumento e substituí-lo no resultado
  • "$(echo *)" significa: execute echo * e coloque o resultado em um único argumento para printf ; tem que ser colocado em um único argumento por causa de como printf funciona
por 11.03.2015 / 07:56
1

Tente ...

set file*
printf %s\  "\$@"

Ele irá adicionar uma barra invertida no cabeçalho do array - somente o primeiro elemento. Você pode fazer o mesmo com o final. Você pode obter todos eles divididos em \ barras invertidas como:

printf \%s file*

... ou ...

set file*; IFS=\; printf %s\n "$*"
    
por 11.03.2015 / 08:28
1

Não, bash caractere de escape preserva o valor literal do próximo caractere que segue, da esquerda para a direita, então \* te dá o padrão \* .

Esse padrão é executado Expansão do nome de arquivo , com o Padrão de correspondência .

Portanto, \* é interpretado como todos os arquivos que começam com \ e são seguidos por qualquer coisa. No seu caso, não correspondeu a nada e bash deixou o padrão \* inalterado para echo .

    
por 11.03.2015 / 08:28
0

Acredito que todas as outras respostas para a segunda metade da pergunta ("Que comando devo usar?") Estão errados, se apenas tecnicamente ou em casos de borda. Eu acredito que isso resolve os problemas nas outras respostas:

(set -- *; echo "\$*")

e (FWIW) toda a escrita está sendo feita em um único comando
(ao contrário de printf '\'; printf "%s " *; echo ).

  • Use um subshell para evitar a configuração / alteração dos parâmetros posicionais em um escopo / contexto externo.
  • Ingenuamente, set * define os parâmetros posicionais ( $1 , $2 , $3 , etc…) para os nomes dos arquivos no diretório atual. (Nomes de arquivos que começam com . (ponto) estão incluídos, ou não, com base no fato de a opção dotglob estar definida.)
  • set * pode falhar se houver um arquivo cujo nome comece com - (traço) (e é o primeiro arquivo na lista * ). set -- * lida com isso; -- diz que "tudo depois disso é um argumento e não uma opção / sinalização".
  • $* é a lista de parâmetros posicionais ( $1 $2 $3 … ), com os parâmetros separados por espaço (s) . O \ faz com que \ seja emitido no início da linha, assim como no echo \ * (comando de exemplo na pergunta), mas isso não interfere na expansão do $* subseqüente.
  • $* é a lista de parâmetros posicionais ( $1 $2 $3 … ), com os parâmetros separados pelo primeiro caractere de $IFS . Se houver a possibilidade de o primeiro caractere de $IFS não ser espaço, faça
(set -- *; IFS=" "; echo "\$*")

Opções ligeiramente mais detalhadas incluem

(set -- *; printf "%s\n" "\$*")

e

(set -- *; printf "\%s\n" "$*")

Estes são mais seguros em um ambiente onde echo pode interpretar barras invertidas nas cadeias de caracteres de argumento mesmo sem uma opção -e .

Novamente, adicione IFS=" "; se houver uma possibilidade que o primeiro caractere de $IFS pode não ser espaço.

    
por 28.08.2018 / 06:11
-1

Eu me deparei com um problema de caractere especial que nada disso parecia resolver ao tentar transferir usando um utilitário específico de conexão direta Windoze. Então eu saí com meu clube de homem das cavernas e pude fazer isso funcionar. A questão principal era que o caminho de destino incluía um caractere de escape antes do valor do nome do arquivo dinâmico armazenado como string.

OUTPUT_FILE='echo "my_file_name.txt"'
OUTPUT_DIR='echo "/path/to/file/"'
xfrcmd='echo "path_to_transferutil ${OUTPUT_DIR}${OUTPUT_FILE}" 'X:\Reports\Reconciliation"'
xfrcmd=${xfrcmd}\
xfrcmd=$xfrcmd'echo "${OUTPUT_FILE}' windozefeller 'account,pass' label parm1 parm2;"'
eval $xfrcmd >logdest
    
por 27.08.2018 / 19:04