Eu me deparei com este script:
#! /bin/bash
if (( $# < 3 )); then
echo "$0 old_string new_string file [file...]"
exit 0
else
ostr="$1"; shift
nstr="$1"; shift
fi
echo "Replacing \"$ostr\" with \"$nstr\""
for file in $@; do
if [ -f $file ]; then
echo "Working with: $file"
eval "sed 's/"$ostr"/"$nstr"/g' $file" > $file.tmp
mv $file.tmp $file
fi
done
Qual é o significado das linhas em que elas usam shift
? Presumo que o script deve ser usado com pelo menos argumentos, então ...?
shell-script
arguments
Patryk
fonte
fonte
pushd
epopd
).bash
, a pergunta é geral para todas as conchas, portanto o padrão Posix prevalece: pubs.opengroup.org/onlinepubs/9699919799/utilities/…Como comentário goldilocks e referências da humanidade descrever,
shift
atribui novamente os parâmetros de posição ($1
,$2
, etc.) de modo a que$1
assume o valor antigo$2
,$2
assume o valor$3
, etc. * O valor antigo$1
é descartado. ($0
não é alterado.) Alguns motivos para fazer isso incluem:$10
não funciona - é interpretado como$1
concatenado com um0
(e, portanto, pode produzir algo comoHello0
). Depois de ashift
, o décimo argumento se torna$9
. (No entanto, na maioria das conchas modernas, você pode usar${10}
.)for
é muito melhor para isso.$1
e$2
são cadeias de texto, enquanto$3
e todos os outros parâmetros são nomes de arquivos.Então, aqui está como isso acontece. Suponha que seu script seja chamado
Patryk_script
e seja chamado comoO script vê
A instrução
ostr="$1"
define a variávelostr
comoUSSR
. A primeirashift
instrução altera os parâmetros posicionais da seguinte maneira:A instrução
nstr="$1"
define a variávelnstr
comoRussia
. A segundashift
instrução altera os parâmetros posicionais da seguinte maneira:E então as
for
mudanças de loopUSSR
($ostr
) paraRussia
($nstr
) nos arquivosTreaty1
,Atlas2
ePravda3
.Existem alguns problemas com o script.
Se o script for chamado como
vê
mas, porque
$@
não é citado, o espaçoWorld Atlas2
não é citado, eofor
ciclo pensa que tem quatro arquivos:Treaty1
,World
,Atlas2
, ePravda3
. Isso deve ser(para citar qualquer caractere especial nos argumentos) ou simplesmente
(que é equivalente à versão mais longa).
Não é necessário que isso seja uma
eval
passagem de entrada desmarcada do usuário para umaeval
pode ser perigosa. Por exemplo, se o script for chamado comovai executar
rm *
! Essa é uma grande preocupação se o script puder ser executado com privilégios mais altos que os do usuário que o invoca; por exemplo, se pode ser executado viasudo
ou invocado a partir de uma interface da web. Provavelmente não é tão importante se você apenas usá-lo como você mesmo, em seu diretório. Mas pode ser alterado paraIsso ainda tem alguns riscos, mas são muito menos graves.
if [ -f $file ]
,> $file.tmp
emv $file.tmp $file
devem serif [ -f "$file" ]
,> "$file.tmp"
emv "$file.tmp" "$file"
, respectivamente, manipular nomes de arquivos que possam ter espaços (ou outros caracteres engraçados) neles. (Oeval "sed …
comando também gerencia nomes de arquivos que possuem espaços.)*
shift
aceita um argumento opcional: um número inteiro positivo que especifica quantos parâmetros devem ser alterados. O padrão é um (1
). Por exemplo,shift 4
causas$5
para se tornar$1
,$6
se tornar$2
e assim por diante. (Observe que o exemplo no Guia Bash para iniciantes está errado.) E, portanto, seu script pode ser modificado para dizero que pode ser considerado mais claro.
Nota final / Aviso:
A linguagem do prompt de comando do Windows (arquivo em lote) também suporta um
SHIFT
comando, que basicamente faz a mesma coisa que oshift
comando nos shells do Unix, com uma diferença marcante, que vou esconder para tentar impedir que as pessoas se confundam com ele:fonte
shift
pode ser aplicado awhile
,until
e até mesmofor
loops para manipular matrizes de maneiras muito mais sutis do que umfor
loop simples . Muitas vezes eu achar que é útil emfor
loops como ...set -f -- $args; IFS=$split; for arg do set -- $arg; shift "${number_of_fields_to_discard}" && fn_that_wants_split_arg "$arg"; done
A explicação mais simples é essa. Considere o comando:
Sem a ajuda de
shift
você, não é possível extrair o valor completo do local, pois esse valor pode ser arbitrariamente longo. Quando você alterna duas vezes, os argumentosSET
elocation
são removidos de forma que:Dê uma olhada longa na
$@
coisa. Isso significa que ele pode salvar o local completo em variável,x
apesar dos espaços e vírgulas que ele possui. Então, em resumo, chamamosshift
e depois recuperamos o valor do que resta da variável$@
.ATUALIZAÇÃO Estou adicionando abaixo um trecho muito curto, mostrando o conceito e a utilidade
shift
sem os quais, seria muito, muito difícil extrair os campos corretamente.Explicação : Após o
shift
, a variável b deve conter o restante do material que está sendo passado, independentemente de espaços ou etc. A[ -n "$b" ] && echo $b
proteção é tal que só imprimimos b se tiver um conteúdo.fonte
"$*"
vez de"$@"
. (3) Eu ia aprovar sua resposta, mas não o fiz, porque você diz "mudar duas vezes", mas na verdade não a mostra em código. (4) Se você deseja que um script consiga obter um valor de várias palavras na linha de comando, provavelmente é melhor exigir que o usuário coloque o valor inteiro entre aspas. ... (continua)Philippines 6014
), espaços à esquerda, espaços à direita ou guias. (5) Aliás, você também pode fazer o que está fazendo aqui no bashx="${*:3}"
.shift
comando. Talvez você esteja se referindo a diferenças sutis $ @ versus $ *. Mas o fato ainda permanece: meu snippet acima pode extrair com precisão o local, independentemente de quantos espaços ele esteja à direita ou na frente, não importa. Após o turno, o local conterá o local correto.shift
tratar os argumentos da linha de comando como uma fila FIFO, ele remove o elemento toda vez que é chamado.fonte