Ignore "sem correspondências" do zsh ao usar a expansão de chaves com glob *.{a,b,}test

Ignore "sem correspondências" do zsh ao usar a expansão de chaves com glob *.{a,b,}test

Gostaria de listar todos os arquivos em uma pasta chamada que foldernamepossui a extensão testou atest.btest

Meu pensamento imediato foi correrls ./foldername/*.{a,b,}test

Isso funciona bem, a menos que não haja nada com a extensão atest; nesse caso, recebo o erro zsh: no matches found: ./foldername/*.atest.

Existe alguma maneira de simplesmente ignorar esse erro e imprimir os arquivos que existem?

Preciso que isso funcione tanto no zsh quanto no Bash.

Responder1

Em

ls -d ./foldername/*.{a,b,}test

{a,b,...}não é um operador glob, é uma expansão de chaves, que é primeiro expandida para:

ls -d ./foldername/*.atest ./foldername/*.btest ./foldername/*.test

E cada glob foi expandido individualmente e, se algum glob não corresponder, o comando será cancelado conforme esperado zsh(ou fish; in bash, você precisa da failglobopção para obter um comportamento semelhante).

Aqui, você deseja usar um único glob que corresponda a todos esses arquivos e cancelar o comando apenas se esse glob não corresponder a nenhum arquivo:

ls -d ./foldername/*.(a|b|)test

Você não deseja usar nullglob, como se nenhum dos globs correspondesse, ele seria executado lssem argumentos, então liste o diretório atual. cshnullglobé melhor nesse aspecto, pois remove globos não correspondentes, mas ainda cancela o comando se todos os globos não corresponderem.

Você não gostaria de usar nonomatch, pois isso lhe daria um comportamento quebrado, basho que seria uma pena.

Para uma alternativa glob que funcione em zshand bash, você pode usar os ksh globs ( set -o kshglobin zshe shopt -s extglobin bash).

Então, você faria:

ls -d ./foldername/*.@(a|b|)test

ou:

ls -d ./foldername/*.?([ab])test

Adicione a failglobopção bashpara evitar que o glob seja passado literalmente lsquando não corresponder.

VerPor que nullglob não é padrão?Para maiores informações.

Responder2

Talvez seja melhor fazer isso com find:

find ./foldername -maxdepth 1 -name '*.atest' -o -name '*.btest' -o -name '*.test'

Responder3

Eu enfrentei esse problema e primeiro o resolvi hackeando o próprio ls. Quando terminei, percebi que tudo que eu precisava era do programa trival C:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
  struct stat statbuf;
  int i;
  for( i=1; i<argc; i++ ) {
    if( stat(argv[i],&statbuf) == 0 ) {
      printf("%s\n",argv[i]);
    }
  }
}

Tudo o que isso faz, e tudo o que precisa fazer, é percorrer seus argumentos de linha de comando e ecoar aquelas strings que correspondem a arquivos válidos (isto é, para os quais stat() é bem-sucedido).

Claro, podemos emular isso com Python, Perl igualmente simples ou até mesmo uma função shell que executa stat, mas escrevê-lo em C torna sua execução muito mais rápida.

Quanto a hackear ls, dei uma olhada em meu wiki pessoal:http://linux.chalisque.net/LsIgnoreMissing

Responder4

O comentário de @DopeGhoti funcionou para mim.

ou seja, redirecionando o erro para /dev/nullusar 2>o operador assim:

ls ./foldername/*.{a,b,}test 2> /dev/null

informação relacionada