Existe uma combinação de shopt glob ou configuração que se comporta como tcsh?

5

Estou fazendo a transição de um usuário tcsh de longa data para um novo usuário bash (está muito atrasado). Eu escrevi um monte de foreach loops no tcsh on the fly em uma base regular, então eu aprendi a sintaxe para loops for bash do bash como um substituto, mas fiquei surpreso quando os padrões glob não-correspondentes passaram pelo loop como strings literais. Eu procurei uma maneira de alterar esse comportamento para que sequências literais fossem ignoradas e encontrassem shopt -s nullglob . Meu entendimento era que isso teoricamente deveria ser equivalente ao modo como tcsh se comporta, mas hoje descobri uma diferença. Quando eu faço ls ../*.doesnotmatch , o resultado foi uma lista do conteúdo do diretório atual. Especificamente, eu fiz isso:

bash:

$ shopt -s nullglob
$ ls ../*.sam
extractSplitReads_BwaMem    extractSplitReads_BwaMem.xml
$ shopt -u nullglob
$ ls ../*.sam
ls: ../*.sam: No such file or directory

Não há nada no diretório pai que corresponda a *.sam , particularmente no diretório atual. Eu estava realmente confuso no início, mas depois percebi que o padrão glob estava desaparecendo e que o comando estava sendo executado como se eu não tivesse fornecido nenhum argumento, por exemplo:

$ ls

Por isso, tentei definir o failglob tanto por si próprio quanto com o nullglob, mas, enquanto o failglob for definido, qualquer padrão glob não correspondente eliminará o comando, independentemente de haver ou não um padrão correspondente:

bash:

$ shopt -s failglob
$ shopt -s nullglob
$ ls ../vis*/*.xml
../visualization/LAJ.xml
$ ls ../vis*/*.xml ../*.sam
bash: no match: ../*.sam
$ ls ../{vis*/*.xml,*.sam}
bash: no match: ../*.sam

Quando estou usando o tcsh, todos os globs são resumidos a apenas aquelas coisas que correspondem e, se nada corresponder, você recebe um erro glob:

tcsh:

$ ls ../vis*/*.xml ../*.sam
../visualization/LAJ.xml
$ ls ../{vis*/*.xml,*.sam}
../visualization/LAJ.xml
$ ls ../*.sam
ls: No match.

Examinei as configurações do shopt, mas não vejo uma maneira de obter esse comportamento. Estou esquecendo de algo? Existe outro shell moderno além de bash ou tcsh que trata globs como o tcsh faz? Eu quero o comportamento do nullglob quando algo corresponde, mas o comportamento do failglob quando nada corresponde, o que parece ser como o tcsh funciona.

    
por hepcat72 07.08.2018 / 01:49

1 resposta

2

As únicas opções shopt relacionadas à expansão de nome de arquivo são dotglob , failglob , nocaseglob e nullglob e nenhuma delas (sozinha ou combinada) parece fazer exatamente o que você deseja. É uma pena porque parece uma boa ideia.

Minha recomendação é ter failglob definido em sessões interativas, para evitar comandos potencialmente indesejados, como:

mv -r file1 file2 dir1 dir2 destination-*-dir

onde file1 , file2 e dir1 seriam movidos para dir2 se destination-*-dir não corresponder a nada e nullglob for definido.

Por outro lado, não é uma boa idéia confiar inteiramente em expansões de nomes de arquivos quando scripts de shell. É recomendável sempre validar se tais expansões existem e são o que deveriam ser.

Quero dizer, em vez de fazer isso:

rm -- *.jpg *.txt

É melhor fazer algo assim:

for file in *.jpg *.txt; do
  if [ -f "${file}" ]; then
    rm -- "${file}"
  fi
done

# Or this (non-POSIX, as it uses an array)

for file in *.jpg *.txt; do
  if [ -f "${file}" ]; then
    files_to_delete+=( "${file}" )
  fi
done

if [ "${#files_to_delete[@]}" -gt 0 ]; then
  rm -- "${files_to_delete[@]}"
fi

Dessa forma, você estará seguro, mesmo que, por exemplo, alguns arquivos correspondam a *.txt , mas na verdade é um diretório.

    
por 07.08.2018 / 05:52