Expande glob com o flag inserido antes de cada nome de arquivo

7

Eu tenho um programa que espera argumentos na seguinte sintaxe:

prog [-f filename | -g filename1 filename2] ...

Cada nome de arquivo deve ser prefixado com o -f flag. Por exemplo, as seguintes são invocações válidas de prog :

prog -f a.txt -g b.txt c.txt -f d.txt
prog -g a.txt b.txt -g c.txt d.txt
prog -f a.txt -f b.txt -f c.txt

... mas os seguintes não são:

prog -f a.txt b.txt
prog -f a.txt -g b.txt
prog a.txt

No meu caso, só me importo com a opção -f .

Eu tenho muitos arquivos em um diretório, e todos terminam em .txt . Eles se parecem com isso:

important-files/
├── a.txt
├── b.txt
├── c.txt
├── d.txt
└── filename with spaces.txt

Eu gostaria de evitar a necessidade de listar todos os arquivos, um por um. Normalmente, eu usaria uma globagem direta para isso:

$ prog important-files/*.txt

Mas isso não funciona, pois produz a seguinte invocação inválida:

$ prog important-files/a.txt important-files/b.txt important-files/c.txt important-files/d.txt 'important-files/filename with spaces.txt'

… quando eu realmente quero essa invocação:

$ prog -f important-files/a.txt -f important-files/b.txt -f important-files/c.txt -f important-files/d.txt -f 'important-files/filename with spaces.txt'

... já que cada nome de arquivo deve ser prefixado com -f para que prog entenda que eles não devem ser interpretados como -g .

Qual é o caminho mais curto para usar um glob e prefixar cada um dos arquivos que ele expande com um flag?

    
por Alexis King 22.05.2018 / 23:24

6 respostas

12

Usando printf e xargs suportando entrada delimitada por nul:

printf -- '-f
printf -- '-f%pre%%s%pre%' important-files/*.txt | xargs -0 prog
%s%pre%' important-files/*.txt | xargs -0 prog

printf faz um loop na string de formatação sobre os argumentos, portanto, para cada nome de arquivo na expansão do glob, ele imprimirá -f e o nome do arquivo será separado pelo caractere nulo. xargs , em seguida, lê isso e converte em argumentos para prog . O -- necessário, pois algumas implementações de printf têm problemas com - na string de formatação. Como alternativa, o - pode ser substituído por 5 , que é padrão.

Mesmo princípio da resposta do Rakesh , mas usando diretamente a expansão do curinga.

    
por 23.05.2018 / 05:13
9

Com o bash, tente:

args=()
for f in important-files/*.txt
do
    args+=(-f "$f")
done
prog "${args[@]}"

Observe que isso não funcionará apenas com nomes de arquivos que contêm espaços em branco, mas também com nomes de arquivos que contenham aspas simples ou duplas ou até mesmo novas linhas.

Uso interativo fácil

Para facilitar o uso interativo, defina uma função:

progf() { args=(); for f in  "$@"; do args+=(-f "$f"); done; prog "${args[@]}"; }

Esta função pode ser usada da seguinte forma:

progf important-files/*.txt

Para tornar a definição da função permanente, coloque-a no arquivo ~/.bashrc .

    
por 23.05.2018 / 00:04
7

zsh agora tem um qualificador P glob para isso .

Para versões mais antigas, você sempre pode usar:

f=(*.txt)
prog -f$^f

$^f ativa o rcexpandparam opção para a expansão de $f , onde as matrizes são expandidas em um estilo semelhante ao do shell rc .

Em rc (ou zsh -o rcexpandparam ):

f=(*.txt)
prog -f$f

É expandido para prog -fa.txt -fb.txt , um pouco como se você tivesse escrito em csh (ou outros shells que suportam a expansão de chaves): prog -f{a.txt,b.txt} .

(note que é diferente de *.txt(P:-f:) em que o nome do arquivo está colado à opção -ffile.txt ao invés de passar dois argumentos -f file.txt . Muitos comandos incluindo todos aqueles que usam o padrão getopt() API para analisar as opções suportam ambas as maneiras, passando argumentos para opções).

fish também expande as matrizes assim:

set f *.txt
prog -f$f

Em bash , você pode emulá-lo com:

f=(*.txt)
prog "${f[@]/#/-f}"

com ksh93 :

f=(*.txt)
prog "${f[@]/*/-f
o=-f f=(*.txt); prog ${o:^^f}
}"

Para -f e o file.txt s correspondentes a serem passados como argumentos separados, outra opção com zsh usando seu operador de zip de matriz:

o=(-g '') f=(*.txt); prog ${o:^^f}

Você pode estender isso para sua opção -g . Supondo que haja um número par de arquivos txt :

printf -- '-g
f=(*.txt)
prog -f$^f
%s
f=(*.txt)
prog -f$f
%s
set f *.txt
prog -f$f
' *.txt | xargs -r0 prog

Passaria -g opções com dois arquivos por vez. Semelhante ao que você teria:

f=(*.txt)
prog "${f[@]/#/-f}"
    
por 23.05.2018 / 11:24
6

Como já mencionado em um comentário:

zsh -c 'prog *.txt(P:-f:)'

Isso usa o qualificador de glob P que preenche uma palavra separada para cada jogo glob. Como quase todos os qualificadores glob do zsh, o bash não tem nada semelhante.

Se você deseja ter conclusão para prog , execute zsh interativamente e digite prog *.txt(P:-f:) . A primeira vez que você executar o zsh interativamente, ele solicitará que você defina uma configuração. Se você não se preocupa com o zsh, escolha a opção para criar .zshrc em branco (mas você perderá alguns recursos zsh que precisam ser ativados explicitamente) ou escolha 1 e passe pelos menus para ativar os padrões recomendados.

    
por 23.05.2018 / 11:01
5

Um. método para fazer isso é com o duo GNU find / xargs:

cd important-files && \
find . -name '*.txt'  -printf '-f
cd important-files && \
find . -name '*.txt'  -printf '-f%pre%%p%pre%' | xargs -0 -r  prog
%p%pre%' | xargs -0 -r prog
    
por 23.05.2018 / 02:00
1

POSIXly, você sempre pode definir uma função como:

prog_f() (
  for i do
    set -- "$@" -f "$i"
    shift
  done
  exec prog "$@"
)
    
por 23.05.2018 / 14:57