expansão shell (A | B) nos nomes de arquivos?

9

É possível expandir uma oropção no shell ao ler um arquivo, por exemplo.

O que quero dizer com isso é que, por exemplo, grepsuporta sintaxe como (A|B)combinar A ou B em um arquivo.

Da mesma forma, se eu tiver esses arquivos:

file1.txt
file2.txt
file3.txt
file4.txt
file5.txt

Eu poderia fazer cat file{1..5}.txtno bash, à medida que expande a gama. Existe uma maneira equivalente de fazer isso com apenas alguns arquivos?

Por exemplo, cat file(1|5).txte apenas imprimir esses 2?

Joe Healey
fonte

Respostas:

16

O padrão de globbing do nome do arquivo padrão para corresponder a um dígito é [0-9]. Isso corresponde a um único dígito:

cat file[0-9].txt

Para selecionar apenas dois deles:

cat file[25].txt

Para números maiores que 9, a expansão de braçadeira será útil (mas veja a nota abaixo para saber a diferença entre padrões de globbing e expansões de braçadeira):

cat file{25..60}.txt

Mais uma vez, a expansão de chaves também permite números individuais:

cat file{12,45,900,xyz}.txt

(observe que no exemplo acima, a expansão de chave não envolve um loop aritmético, mas gera apenas nomes com base nas cadeias fornecidas).

Em bash, com a extglobopção de shell ativada ( shopt -s extglob), o seguinte também funcionará:

cat file@(12|45|490|foo).txt

O @(...)padrão corresponderá a qualquer um dos |padrões delimitados incluídos .


A diferença entre os padrões de englobamento como [...]e @(...)e expansões cinta, é que uma expansão cinta é gerado na linha de comando e não pode realmente encontrou nenhum nomes existentes no diretório atual. Um padrão globbing de nome de arquivo corresponderá a nomes, mas o shell não reclamará se nem todo o nome possível existir. Se não existir um nome correspondente, o padrão permanecerá sem expansão, a menos que também nullglobseja definida a opção shell, nesse caso o padrão será removido.

Exemplo:

touch file1

ls file[0-9]

Aqui, apenas a lista de arquivos file1será exibida.

Com ls file{0..9}, lsiria reclamar por não encontrar file0, file2etc.

No exemplo a seguir, o primeiro comando tocará apenas nos nomes existentes que correspondem ao padrão especificado, enquanto a segunda linha criará arquivos que ainda não existem:

touch file[0-9]

touch file{0..9}
Kusalananda
fonte
@ thecarpy Sinto muito, mas esse não é um padrão que corresponda, por exemplo file45.txt. A expressão entre colchetes [...]funciona exatamente como na expressão regular, mas é usada !no lugar de ^para dizer "not in". Um [...]padrão sempre corresponderá a um único caractere.
Kusalananda
Você está certo! BTW, o meu {1,2}também não é compatível com POSIX ... aprendi algumas coisas novas hoje!
Thecarpy
2
O fato de que expansões entre chaves não precisem corresponder a arquivos reais pode ser extremamente útil, se você estiver usando para (por exemplo) gerar padrões para passar grep, gerar URLs para passar curle assim por diante ... mas também pode ser confuso para pessoas acostumadas a trabalhar com globs.
Kevin
@Kevin Correct. Uma expansão de colchete não precisa ser sobre nomes de arquivos.
Kusalananda
7

A sintaxe a ser usada é a file{1,2}que avaliará para file1e file2.

$ ls
$ touch file{1,2,3,4,5,6,7,8,9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

Como Inian também apontou abaixo ... seria mais fácil fazer touch file{1..9}neste caso de exemplo ...

$ ls
$ touch file{1..9}
$ ls
file1  file2  file3  file4  file5  file6  file7  file8  file9

Você também pode usar várias expressões, como:

$ ls
$ touch file{1..9}{a..z}
$ ls
file1a file1b file1c
[...]
file9x file9y file9z

Sim, o acima criará 234 arquivos ( 9vezes 26).

thecarpy
fonte
1
Essa file{1,2} sintaxe também é conveniente para renomear arquivos:mv some_very_long_filename.txt{,.bak}
Eric Duminil
6

Sim, você pode usar a expansão de chaves no bashshell. Para apenas alguns arquivos, basta fazer file{1..2}ou apenasfile{1,2}

Ou se você estiver preocupado com a falta de arquivos em alguns casos, basta fazer um loop simples,

for file in file{1..4}.txt; do
    [ -f "$file" ] || continue
    echo "$file" # Do other actions on file here
done

Ou, se apenas concatenar é sua única operação nos arquivos e se você não tiver certeza de que arquivos não poderiam estar presentes em nenhum momento, apenas cateles e suprima os erros. Redirecionar o erro padrão para /dev/nullsuprimir os erros se o arquivo não estiver disponível.

cat file{1,5}.txt 2>/dev/null

ou use a expressão glob file[15]que não reclama dos erros se o arquivo não foi encontrado.

cat file[15].txt
Inian
fonte
2
Não, não há preocupações sobre os arquivos não estarem lá, eu só queria mostrar o conteúdo de 2 arquivos numerados numericamente, mas que não são consecutivos, então a file{1,5}sintaxe da vírgula era tudo o que estava faltando!
9138 Joe Healey