Eu estudei sobre a linha de comando e aprendi que |
(pipeline) destina-se a redirecionar a saída de um comando para a entrada de outro. Então, por que o comando ls | file
não funciona?
file
input é um dos mais nomes de arquivos, como file filename1 filename2
ls
output é uma lista de diretórios e arquivos em uma pasta, então pensei ls | file
em mostrar o tipo de arquivo de cada arquivo em uma pasta.
Quando eu uso, no entanto, a saída é:
Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
[-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
file -C [-m magicfiles]
file [--help]
Como houve algum erro com o uso do file
comando
command-line
ls
pipe
file-command
IanC
fonte
fonte
ls
, isso indica que você deseja que todos os arquivos no diretório atual sejam tratados com ofile
comando ... Então, por que simplesmente não fazer:,file *
que responderá com uma linha para cada arquivo, pasta.file *
é a maneira mais inteligente, eu só estava me perguntando por que usar als
saída não estava funcionando. Dúvida apuradas :)ls
geralmente é uma má ideia.Respostas:
A questão fundamental é que
file
espera nomes de arquivos como argumentos de linha de comando, não no stdin. Quando você escreve,ls | file
a saída dels
está sendo passada como entrada parafile
. Não como argumentos, como entrada.Qual é a diferença?
Os argumentos da linha de comando são quando você escreve sinalizadores e nomes de arquivos após um comando, como em
cmd arg1 arg2 arg3
. Em scripts shell esses argumentos estão disponíveis como as variáveis$1
,$2
,$3
, etc. Em C você acessá-los através dochar **argv
eint argc
argumentos paramain()
.A entrada padrão, stdin, é um fluxo de dados. Alguns programas gostam
cat
ouwc
lêem do stdin quando não recebem argumentos da linha de comando. Em um script de shell, você pode usarread
para obter uma única linha de entrada. Em C você pode usarscanf()
ougetchar()
, entre várias opções.file
normalmente não lê de stdin. Espera que pelo menos um nome de arquivo seja passado como argumento. É por isso que imprime o uso quando você escrevels | file
, porque você não passou nenhum argumento.Você pode usar
xargs
para converter stdin em argumentos, como emls | xargs file
. Ainda assim, como menciona Terdon , analisarls
é uma má ideia. A maneira mais direta de fazer isso é simplesmente:fonte
file
a obter nomes de arquivos de sua entrada, usandols | file -f -
. Ainda é uma péssima ideia de c.ls
a saída parafile
o stdin. Experimente.file $(ls)
, o que também funciona, de outra maneira.Porque, como você diz, a entrada de
file
deve ser um nome de arquivo . A saída dels
, no entanto, é apenas texto. O fato de ser uma lista de nomes de arquivos não altera o fato de ser apenas texto e não o local dos arquivos no disco rígido.Quando você vê a saída impressa na tela, o que vê é texto. Se esse texto é um poema ou uma lista de nomes de arquivos, não faz diferença para o computador. Tudo o que sabe é que é texto. É por isso que você pode passar a saída
ls
para programas que recebem texto como entrada (embora você realmente não deva ):Portanto, para usar a saída de um comando que lista nomes de arquivos como texto (como
ls
oufind
) como entrada para um comando que utiliza nomes de arquivos, é necessário usar alguns truques. A ferramenta típica para isso éxargs
:Como eu disse antes, porém, você realmente não deseja analisar a saída de
ls
. Algo comofind
é melhor (o queprint0
imprime uma em\0
vez de uma nova coluna após cada nome de arquivo e o-0
dexargs
permite lidar com essa entrada; esse é um truque para fazer com que seus comandos funcionem com nomes de arquivos que contêm novas linhas):Que também tem sua própria maneira de fazer isso, sem a necessidade de
xargs
:Finalmente, você também pode usar um loop de shell. No entanto, observe que, na maioria dos casos,
xargs
será muito mais rápido e mais eficiente. Por exemplo:fonte
file
não parece realmente ler stdin a menos dado um explícito-
marcador: compararfile foo
,echo foo | file
eecho foo | file -
; na verdade, isso é provavelmente a razão para a mensagem de uso no caso PO (ou seja, não é realmente porque a saída dels
é "simplesmente texto", mas sim porque a lista de argumentos parafile
está vazia)echo foo | file -
, na verdade, não é executadofile
no arquivo,foo
mas no fluxo stdin.cat
exceto stdin sem,-
exceto quando dados argumentos de arquivo também, eu acho?Ele não "redireciona" a saída, mas pega a saída de um programa e a usa como entrada, enquanto o arquivo não recebe entradas, mas os nomes de arquivos como argumentos , que são então testados. Os redirecionamentos não passam esses nomes de arquivo como argumentos, nem a tubulação , quanto mais tarde você estiver fazendo.
O que você pode fazer é ler os nomes de arquivos de um arquivo com a
--files-from
opção se você tiver um arquivo que liste todos os arquivos que você deseja testar; caso contrário, apenas passe os caminhos para seus arquivos como argumentos.fonte
A resposta aceita explica por que o comando pipe não funciona imediatamente e, com o
file *
comando, oferece uma solução simples e direta.Gostaria de sugerir outra alternativa que possa ser útil em algum momento. O truque é usar o
(`)
caractere backtick . O backtick é explicado em grandes detalhes aqui . Em resumo, ele pega a saída do comando incluído nos backticks e a substitui como uma string no comando restante.Então,
find `ls`
pegará a saída dols
comando e a substituirá como argumentos para ofind
comando. Isso é mais longo e mais complicado que a solução aceita, mas variantes disso podem ser úteis em outras situações.fonte
command
(não pode encontrar o código de barra invertida no meu telefone) para expandir a saída de um comando no bash e usá-lo como parâmetro para outros comandos. Realmente útil, embora o uso nesse caso (com ls ) ainda resulte em alguns problemas devido aos caracteres especiais em alguns nomes de arquivos.A saída de
ls
um canal é um sólido bloco de dados com 0x0a separando cada linha - isto é, um caractere de avanço de linha - efile
obtém isso como um parâmetro, onde espera que vários caracteres funcionem um de cada vez.Como regra geral, nunca use
ls
para gerar uma fonte de dados para outros comandos - um dia ela será direcionada para ...rm
e você terá problemas!Melhor usar um loop, como o
for i in *; do file "$i" ; done
que produzirá a saída desejada, previsivelmente. As aspas existem no caso de nomes de arquivos com espaços.fonte
file *
;-)ls
uma idéia muito, muito ruim . Não apenas porque você pode passá-lo para algo prejudicial, comorm
, mais importante, porque ele quebra em qualquer nome de arquivo não padrão.rm
nomes de arquivos da entrada padrão? Eu acho que não. Além disso, como regra geral,ls
tem sido um dos principais exemplos de uma fonte de dados para o uso de pipelines Unix desde o início do Unix. É por isso que o padrão é um simples nome de arquivo por linha sem atributos ou adornos quando a saída é um canal, diferente da formatação padrão usual quando a saída é o terminal.Se você deseja usar um pipe para alimentar,
file
use a opção-f
que normalmente é seguida por um nome de arquivo, mas você também pode usar um único hífen-
para ler em stdin, portantoO truque com o hífen
-
funciona com muitos utilitários de linha de comando padrão (embora--
algumas vezes), por isso vale sempre a pena tentar.A ferramenta
xarg
é muito mais poderosa e, na maioria dos casos, é necessária apenas se a lista de argumentos for muito longa (consulte esta publicação para obter detalhes).fonte
--
? Eu nunca vi isso.--
normalmente é o indicador "fim de sinalizadores".Funciona com o comando use abaixo
Funcionará melhor para mim
fonte
Isso também deve funcionar:
como também discutido aqui: https://unix.stackexchange.com/questions/5778/whats-the-difference-between-stuff-and-stuff
fonte
$()
método é o mesmo que o método backtick em askubuntu.com/a/795744/158442