sai de um script se uma linha ls não encontrar nenhuma correspondência

3

Estou escrevendo este script com esta linha:

ls -lrt /dir1/dir2/filename*.txt | tail -1 | awk '{print $9}' | read variable

O que eu queria é sair do script (sem usar if-statement) se ele não encontrar correspondência.

    
por area51 14.08.2014 / 04:20

2 respostas

1

Como escrever este comando

Esta tarefa em particular não requer um pipe.

No zsh:

a=(/dir1/dir2/filename*.txt(Nom[1]))
if ((! #a)); then
  echo >&2 "No file matches /dir1/dir2/filename*.txt"
  exit 2
fi
variable=$a[1]

ou, para sair do script automaticamente se a glob não corresponder:

set -e
a=(/dir1/dir2/filename*.txt(om[1]))
variable=$a[1]

Outros shells não possuem um recurso interno para encontrar o arquivo mais recente. Chamar ls é razoável desde que seus nomes de arquivo contenham apenas caracteres imprimíveis diferentes de novas linhas, mas não passe a opção -l e depois analise todos, exceto o nome, que é gratuitamente complexo e frágil (quebra de espaços, em particular ). Além disso, obtenha a primeira correspondência da -t sort, mais rápido do que a última correspondência da -tr sort. A abordagem direta:

variable=$(ls -td /dir1/dir2/filename*.txt 2>/dev/null | head -n 1)
if [ -z "$variable" ]; then
  echo >&2 "No file matches /dir1/dir2/filename*.txt"
  exit 2
fi

Manuseio de tubos

Se você quiser abortar seu script quando o lado esquerdo de um pipeline falhar (ou seja, sair com um status diferente de zero ou devido a um sinal), em ksh93 e bash, você pode definir a opção pipefail e sair se o status do pipeline é diferente de zero.

set -e -o pipefail
somecommand | filter
echo "somecommand succeeded"

ou

set -o pipefail
if ! somecommand | filter; then
  echo >&2 "somecommand or filter failed"
  exit 2
fi

O Zsh não possui a opção pipefail , mas você pode recuperar o código de status de cada componente do pipeline na matriz pipestatus .

somecommand | filter
if ((pipestatus[1])); then
  echo >&2 "somecommand failed"
  exit 2
fi

Em outros shells, o status do pipeline é o status do comando à direita. Não há nenhuma maneira direta de recuperar o status do comando da esquerda. Consulte Obter o status de saída do processo canalizado para outro para algumas soluções possíveis.

Tenha em atenção que filter deve ler toda a saída de somecommand , caso contrário, terá de lidar com o caso em que somecommand morre de um SIGPIPE .

Se você quiser agir com base no fato de somecommand produzir alguma saída, em vez de basear-se no status de saída, use ifne de moreutils de Joey Hess . Observe que a maioria dos sistemas não possui esses utilitários instalados por padrão.

    
por 15.08.2014 / 00:54
0
{   ls -lrt /dir1/dir2/filename*.txt || 
    kill $$ 
} | tail -1 | 
awk '{print $9}' | 
read variable

Apenas kill você mesmo. Às vezes o shell pode ser um pouco choroso quando eu faço isso, mas eu acho que ele nem choraminga se eu fizer kill -PIPE $$ .

Ou se preferir ser específico sobre os códigos de retorno e o restante:

trap 'echo some error >&2; exit 1' USR1 $and_or_other_signals
{ ls ... || kill -USR1 $$; } |...

Em relação ao resto .. Talvez de outra forma ..?

touch ~/"a n\$ew file
in 'my home
directory"
v=$(ls -1rdt ~/* || kill -PIPE $$; echo /)
v=${v%?/*}; v=${v##*/}
printf '<%s>' ~/"$v"

OUTPUT

</home/mikeserv/a n$ew file
in 'my home
directory>

Isso lhe dará o nome do último arquivo modificado em qualquer diretório, independentemente de quaisquer caracteres que ele possa conter. Eu uso o < > para indicar o início e o fim da variável e mostrar que ela não contém nenhum espaço em branco à direita. E o echo bit é para garantir que, se houver , ele persistirá.

Definitivamente, bate o pipeline e ainda matará um shell com script se ls retornar diferente de 0 - como quando seus argumentos de linha de comando não existem.

O mesmo pode ser feito sem reverter a ordem de classificação, mas é um pouco mais complicado, e é necessário mudar para o diretório de destino para fazê-lo facilmente:

touch ~/"a n\$ew file
in 'my home
directory"
v=$(cd ~; ls -dtm ./* || kill -PIPE $$)
v=${v#*/}; v=${v%%,?./*}
printf '<%s>' ~/"$v"

OUTPUT

</home/mikeserv/a n$ew file
in 'my home
directory>

Outro método bastante portátil, eu acho:

set /[d]ir1/dir2/filename*.txt
[ -z "${1##/\[*}" ] && exit 1
while [ -n "$2" ] 
do  [ "$1" -nt "$2" ] &&
    set "$@" "$1"
shift; done; v=$1
printf %s\n "$1" "$v"
    
por 15.08.2014 / 08:10