Mais
Como iterar sobre argumentos em um script Bash
Eu tenho um comando complexo que I'gostaria de fazer um script shell/bash de. Eu posso escrevê-lo em termos de $1
facilmente:
foo $1 args -o $1.ext
Eu quero poder passar vários nomes de entrada para o script. Qual'é a maneira correcta de o fazer?
E, claro, eu quero lidar com nomes de arquivo com espaços neles.
830
3
Utilize
"$@"
para representar todos os argumentos:Isto irá iterar sobre cada argumento e imprimi-lo em uma linha separada. $@ comporta-se como $*, exceto que quando citados os argumentos são quebrados corretamente se houver espaços neles:
Re-escrever uma resposta já eliminada resposta por VonC. A resposta sucinta de Robert Gamble lida diretamente com a pergunta. Esta amplifica em alguns assuntos com nomes de arquivo contendo espaços. Veja também: ${1:+"$@"} in /bin/sh Tese básica:
"$@"
está correta, e$*
(não cotada) está quase sempre errada. Isto porque"$@"
funciona bem quando os argumentos contêm espaços, e funciona da mesma forma que$*
quando não funciona. Em algumas circunstâncias,"$*"
também funciona bem, mas"$@"
geralmente (mas não sempre) trabalha nos mesmos lugares. Não cotados,$@
e$*
são equivalentes (e quase sempre errados). Então, qual é a diferença entre$*
,$@
,"$*"
, e"$@"
? Todos eles estão relacionados com "todos os argumentos para a concha", mas fazem coisas diferentes. Quando não citados,$*
e$@
fazem a mesma coisa. Eles tratam cada 'palavra' (sequência de espaços não citados) como um argumento separado. As formas citadas são bem diferentes, no entanto:"$*"
trata a lista de argumentos como uma única string separada por espaço, enquanto"$@"
trata os argumentos quase exatamente como eles eram quando especificados na linha de comando."$@"
expande para nada quando não há argumentos posicionais;"$*"
expande para uma string &mdash vazia; e sim, há uma diferença, embora possa ser difícil percebê-la. Veja mais informações abaixo, após a introdução do comando (não-padrão)al
. Tese secundária: se você precisar processar argumentos com espaços e depois passe-os para outros comandos, então às vezes você precisa de comandos não-padronizados ferramentas para ajudar. (Ou você deve usar arrays, com cuidado:"${array[@]}"
comporta-se analogamente a"$@"
.) Exemplo:Porque é que isso não funciona? Não funciona porque os processos de citação de conchas antes de se expandirem variáveis. Então, para que a shell preste atenção às aspas embutidas em
$var
, você tem que usar o "eval":Isso fica muito complicado quando você tem nomes de arquivos como "Ele disse, "Não faças isso!" (com aspas e aspas duplas e espaços).
As conchas (todas elas) não facilitam particularmente o manuseio de tais coisas, então (curiosamente) muitos programas Unix não fazem um bom trabalho de a lidar com eles. No Unix, um nome de arquivo (componente único) pode conter quaisquer caracteres, exceto e NUL
'\0'``. No entanto, as conchas encorajam fortemente a ausência de espaços ou novas linhas ou separadores. em qualquer lugar em um caminho nomes. É também por isso que os nomes de arquivos Unix padrão não contêm espaços, etc. Ao lidar com nomes de arquivos que podem conter espaços e outros personagens problemáticos, você tem que ser extremamente cuidadoso, e eu achei há muito tempo que eu precisava de um programa que não é padrão no Unix. Eu o chamo de
escape(a versão 1.1 foi datada de 1989-08-23T16:01:45Z). Aqui está um exemplo do
escapeem uso - com o sistema de controle SCCS. É um script de capa que faz um
delta(pense *check-in*) e um
get(pensa *check-out*). Vários argumentos, especialmente
-y(a razão pela qual você fez a mudança) conteria espaços em branco e linhas novas. Note que o script data de 1992, então ele usa back-ticks ao invés de
$(cmd ...)notação e não utiliza
#!/bin/sh` na primeira linha.(Provavelmente eu não usaria a fuga tão bem hoje em dia - ela é não necessário com o argumento
-e', por exemplo - mas, em geral, isto é um dos meus scripts mais simples utilizando o
escape). O programa
escape' simplesmente produz seus argumentos, como oecho'. mas assegura que os argumentos são protegidos para uso com (um nível de "eval"; eu tenho um programa que fez shell remoto execução, e que era necessário para escapar da saída do
escape`).Eu tenho outro programa chamado
al
que lista seus argumentos um por linha (e é ainda mais antiga: versão 1.1 datada de 1987-01-27T14:35:49). É mais útil na depuração de scripts, pois pode ser ligado a um linha de comando para ver que argumentos são realmente passados ao comando.[Adicionado: E agora para mostrar a diferença entre as várias notas de
"$@"
, aqui está mais um exemplo:Note que nada preserva os espaços em branco originais entre os
*
e*/*
na linha de comando. Note também que você pode alterar os 'argumentos da linha de comando' na shell utilizando:Isto define 4 opções, '-novo', '-opt', 'e', e 'arg com espaço'.
] Hmm, isso é um longo resposta - talvez exegese seja o melhor termo. Código fonte para
escape
disponível a pedido (e-mail para o primeiro ponto do nome último nome no gmail dot com). O código fonte do `al' é incrivelmente simples:É tudo. É equivalente ao script
test.sh' que Robert Gamble mostrou, e poderia ser escrito como uma função shell (mas funções shell não existiam na versão local de Bourne shell quando eu escrevi
al'). Note também que você pode escrever `al' como um simples script shell:O condicional é necessário para que não produza nenhum resultado quando passado sem argumentos. O comando `printf' irá produzir uma linha em branco com apenas o argumento de formatação de string, mas o programa em C não produz nada.
Note que a resposta de Robert está correta, e funciona também em
sh
. Você pode (portabilidade) simplificá-la ainda mais:é equivalente a:
Isto é, não precisas de nada!
Teste (
$
é um prompt de comando):Eu li sobre isso pela primeira vez no Unix Programming Environment de Kernighan e Pike.
Em
bash',
ajuda para' documentar isto: