Quero canalizar nomes de arquivos para outros programas, mas todos eles sufocam quando os nomes contêm espaços.
Digamos que eu tenho um arquivo chamado.
foo bar
Como posso find
retornar o nome correto?
Obviamente eu quero:
foo\ bar
ou:
"foo bar"
Edição : Eu não quero passar xargs
, eu quero obter uma seqüência de caracteres formatada corretamente find
para que eu possa canalizar a seqüência de nomes de arquivos diretamente para outro programa.
-exec
bandeira comfind
? você pode potencialmente aliviar esse erro e tornar seu comando mais eficiente, ao-exec
invés de canalizá-lo para outros comandos. Just my $ .02find
formata os nomes dos arquivos; eles são escritos um nome por linha. (Obviamente, isso é ambíguo se um nome de arquivo contiver um caractere de nova linha.) Portanto, o problema é o final de recebimento "sufocado" quando obtém um espaço, o que significa que você precisa nos dizer qual é o final de recebimento se quiser uma resposta significativa. .find
oferecer uma opção para gerar nomes de arquivos em um formato adequado para o shell. Em geral, porém, a extensão-print0
GNUfind
funciona bem para muitos outros cenários (também), e você deve aprender a usá-la em qualquer caso.ls $(command...)
não alimenta a listastdin
. Ele coloca a saída$(command...)
diretamente na linha de comando. Nesse caso, é o shell que está lendo a partir de c e usará o valor atual de$IFS
para decidir como dividir a saída em palavras. Em geral, é melhor usarxargs
. Você não notará um impacto no desempenho.find -printf '"%p"\n'
adicionará aspas duplas ao redor de cada nome encontrado, mas não citará corretamente aspas duplas no nome de um arquivo. Se os nomes de arquivo não tem nenhum aspas duplas incorporadas, você pode ignorar o problema: ou tubo atravéssed 's/"/&&/g;s/^""/"/;s/""$/"/'
. Se os nomes dos arquivos acabarem sendo manipulados pelo shell, provavelmente você deve usar aspas simples em vez de aspas duplas (caso contrário,sweet$HOME
se tornará algo parecidosheet/home/you
). E isso ainda não é muito robusto em relação aos nomes de arquivos com novas linhas. Como você quer lidar com isso?Respostas:
POSIXAMENTE:
Com
find
suportes-print0
exargs
suportes-0
:-0
A opção diz ao xargs para usar o caractere ASCII NUL em vez de espaço para finalizar (separar) os nomes de arquivos.Exemplo:
fonte
ls $(find . -maxdepth 1 -type f -print0 | xargs -0)
eu recebols: cannot access ./foo: No such file or directory
ls: cannot access bar: No such file or directory
$(..)
entre aspas"$(..)"
find
exargs
.O uso
-print0
é uma opção, mas nem todos os programas suportam o uso de fluxos de dados delimitados por nulos, então você terá que usarxargs
a-0
opção para algumas coisas, como observou a resposta do Gnouc.Uma alternativa seria usar
find
's-exec
ou-execdir
opções. O primeiro dos itens a seguir alimentará os nomes dos arquivossomecommand
um por vez, enquanto o segundo será expandido para uma lista de arquivos:Você pode achar que é melhor usar globbing em muitos casos. Se você possui um shell moderno (bash 4+, zsh, ksh), pode obter globbing recursivo com
globstar
(**
). No bash, você deve definir isso:Eu tenho uma linha dizendo
shopt -s globstar extglob
no meu .bashrc, então isso está sempre ativado para mim (e também os globs estendidos, que também são úteis).Se você não quiser recursividade, obviamente use apenas
./*.txt
para usar todos os * .txt no diretório de trabalho.find
possui alguns recursos de pesquisa refinados muito úteis e é obrigatório para dezenas de milhares de arquivos (nesse momento, você encontrará o número máximo de argumentos do shell), mas, para o uso diário, muitas vezes é desnecessário.fonte
Pessoalmente, eu usaria a
-exec
ação find para resolver esse tipo de problema. Ou, se necessárioxargs
, o que permite a execução paralela.No entanto, existe uma maneira de obter
find
uma lista de nomes de arquivos legíveis por bash. Sem surpresa, ele usa-exec
ebash
, em particular, uma extensão doprintf
comando:No entanto, embora isso imprima corretamente palavras com escape de shell, não será utilizável com
$(...)
, porque$(...)
não interpreta aspas ou escapes. (O resultado de$(...)
está sujeito a divisão de palavras e expansão do nome do caminho, a menos que esteja entre aspas.) Portanto, o seguinte não fará o que você deseja:O que você teria que fazer é:
(Observe que não fiz nenhuma tentativa real de testar a monstruosidade acima).
Mas então você também pode:
fonte
ls
cenário capture adequadamente o caso de uso do OP, mas isso é apenas especulação, já que não foram mostrados o que ele está realmente tentando realizar. Esta solução realmente funciona muito bem; Eu obter a saída I (vagamente) esperada para todos os nomes de arquivo engraçadas eu tentei, incluindotouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
eval
é que você ainda não precisa transmiti-laeval
; você pode salvá-lo em um parâmetro e usá-lo mais tarde, talvez várias vezes com comandos diferentes. No entanto, OP não dá nenhuma indicação de que esse é o caso de uso (e se fosse, talvez seja melhor colocar os nomes de arquivos em uma matriz, apesar de que é complicado demais.)você receberá os arquivos e diretórios contém espaços
você receberá os arquivos contém espaços
você receberá os diretórios contém espaços
fonte
fonte