Respostas:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Como você pode ver, o -c
arquivo foi usado como uma opção du
e não é relatado (e você vê a total
linha por causa disso du -c
). Além disso, o arquivo chamado a\n12\tb
está nos fazendo pensar que existem arquivos chamados a
e b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Isso é melhor. Pelo menos esse tempo -c
não é considerado uma opção.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Isso é ainda melhor. O ./
prefixo impede que -c
seja tomado como uma opção e a ausência de ./
antes b
na saída indica que não há b
arquivo, mas há um arquivo com um caractere de nova linha (mas veja abaixo 1 para mais digressões sobre isso).
É uma boa prática usar o ./
prefixo quando possível e, se não, e para dados arbitrários, você sempre deve usar:
cmd -- "$var"
ou:
cmd -- $patterns
Se cmd
não houver suporte --
para marcar o final das opções, você deve denunciá-lo como um bug ao autor (exceto quando for por opção e documentado como para echo
).
Há casos em que ./*
resolve problemas que --
não resolvem . Por exemplo:
awk -f file.awk -- *
falhará se houver um arquivo chamado a=b.txt
no diretório atual (define a variável awk a
para em b.txt
vez de pedir para processar o arquivo).
awk -f file.awk ./*
Não tem o problema porque ./a
não é um nome de variável awk válido, portanto ./a=b.txt
não é considerado uma atribuição de variável.
cat -- * | wc -l
falhará se houver um arquivo chamado -
no diretório atual, conforme indicado cat
na leitura do stdin ( -
é especial para a maioria dos utilitários de processamento de texto e para cd
/ pushd
).
cat ./* | wc -l
está OK porque ./-
não é especial cat
.
Coisas como:
grep -l -- foo *.txt | wc -l
contar o número de arquivos que foo
estão errados porque pressupõe que os nomes dos arquivos não contenham caracteres de nova linha ( wc -l
conta os caracteres de nova linha, os que são gerados por grep
cada arquivo e os nomes dos mesmos). Você deve usar:
grep -l foo ./*.txt | grep -c /
(contar o número de /
caracteres é mais confiável, pois só pode haver um por nome de arquivo).
Para recursivo grep
, o truque equivalente é usar:
grep -rl foo .//. | grep -c //
./*
pode ter alguns efeitos colaterais indesejados.
cat ./*
adiciona mais dois caracteres por arquivo, para que você atinja o limite do tamanho máximo de argumentos + ambiente mais rapidamente. E às vezes você não quer que isso ./
seja relatado na saída. Gostar:
grep foo ./*
Saída:
./a.txt: foobar
ao invés de:
a.txt: foobar
1 . Sinto que tenho que expandir isso aqui, seguindo a discussão nos comentários.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Acima, ./
marcar o início de cada arquivo significa que podemos identificar claramente onde cada nome de arquivo começa (em ./
) e onde termina (na nova linha antes do próximo ./
ou do final da saída).
O que isso significa é que a saída de du ./*
, ao contrário da de du -- *
), pode ser analisada de maneira confiável, embora não tão facilmente em um script.
Quando a saída vai para um terminal, existem muitas outras maneiras pelas quais um nome de arquivo pode enganar você:
\r
move o cursor para o início da linha, \b
move o cursor para trás, \e[C
para frente (na maioria dos terminais) ...Existem caracteres Unicode com a mesma aparência da barra na maioria das fontes
$ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
/ ⁄ ∕ ╱ ⧸
(veja como funciona no seu navegador).
Um exemplo:
$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Muitos x
, mas y
está faltando.
Algumas ferramentas como GNU
ls substituiriam os caracteres não imprimíveis por um ponto de interrogação (observe que ∕
(U + 2215) é imprimível) quando a saída passa para um terminal. GNU du
não.
Existem maneiras de fazê-los se revelar:
$ ls
x x x?0?.∕x y y?0?.?[Cx y?x
$ LC_ALL=C ls
x x?0?.???x x y y?x y?0?.?[Cx
Veja como nos ∕
mudamos ???
depois que dissemos ls
que nosso conjunto de caracteres era ASCII.
$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$
$
marca o final da linha, para que possamos identificar o "x"
vs "x "
, todos os caracteres não imprimíveis e caracteres não ASCII são representados por uma sequência de barra invertida (a barra invertida seria representada com duas barras invertidas), o que significa que é inequívoco. Era o GNU sed
, deveria ser o mesmo em todas as sed
implementações compatíveis com POSIX, mas observe que algumas sed
implementações antigas não são tão úteis.
$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$
(não padrão, mas bastante comum, também cat -A
com algumas implementações). Essa é útil e usa uma representação diferente, mas é ambígua ( "^I"
e <TAB>
é exibida da mesma forma, por exemplo).
$ du -hs ./* | od -vtc
0000000 0 \t . / x \n 0 \t . / x \n 0 \t .
0000020 / x \n 0 \t . 342 210 225 x \n 0 \t . / y
0000040 \r 0 \t . 033 [ C x \n 0 \t . / y \b x
0000060 \n
0000061
Esse é padrão e inequívoco (e consistente de implementação para implementação), mas não é tão fácil de ler.
Você notará que y
nunca apareceu acima. Isso é um completamente alheios problema com du -hs *
que não tem nada a ver com nomes de arquivos, mas devem ser observados: porque du
o uso do disco relatórios, não relatar outros links para um arquivo já listadas (nem todas as du
implementações se comportam como que, embora quando os links de disco rígido são listados na linha de comando).
[a-z0-9.+-]
.
/tmp
) ou em uma área com muitos carros caros ( $HOME
) e é ainda pior ir a um site de perguntas e respostas e dizer que sempre é bom não trancar o carro sem especificar em quais condições (em uma garagem trancada, script você escreveu executar por si mesmo apenas em uma máquina não está ligado a qualquer rede ou armazenamento removível ...)
"b "
ou "a\bb"
) engane um usuário em um terminal, mas não um script que analise a saída du ./*
. Eu provavelmente deveria adicionar uma nota sobre isso. Vai fazer amanhã. Note que anteriormente eu quis dizer privilegiado no sentido geral, não root
(embora isso se aplique ainda mais, é root
claro). novas linhas são permitidas, ignorá-las é um bug. os bugs costumam ser explorados. Você precisa medir o risco caso a caso. Boas práticas de codificação podem evitar os problemas em muitos casos. Certamente no SE, devemos conscientizar.
Não há diferença entre um *
e ./*
em termos de quais arquivos quer lista de vontade. A única diferença seria com o 2º formulário, cada arquivo teria uma barra ./
prefixada na frente deles, o que normalmente significa o diretório atual.
Lembre-se de que o .
diretório é uma notação abreviada para o diretório atual.
$ ls -la | head -4
total 28864
drwx------. 104 saml saml 12288 Jan 23 20:04 .
drwxr-xr-x. 4 root root 4096 Jul 8 2013 ..
-rw-rw-r--. 1 saml saml 972 Oct 6 20:26 abcdefg
Você pode se convencer de que essas duas listas são essencialmente a mesma coisa, usando echo
para ver o que o shell as expandiria.
$ echo *
$ echo ./*
Esses 2 comandos listarão todos os arquivos em seu diretório atual.
Podemos criar alguns dados falsos como:
$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5
Agora, quando usamos os echo
comandos acima , vemos a seguinte saída:
$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5
Essa diferença pode parecer desnecessária, mas há situações em que você deseja garantir às várias ferramentas de linha de comando do Unix que você está passando nomes de arquivos para elas através da linha de comando e nada mais!
Como a resposta de @ Stephane aponta , devido à natureza de quais caracteres são legais ao nomear arquivos e diretórios no Unix, nomes de arquivos perigosos podem ser construídos, com efeitos colaterais inesperados quando transmitidos a vários comandos do Unix na linha de comando.
Com frequência, o uso de ./
será usado para ajudar a garantir que os nomes de arquivos expandidos sejam considerados como nomes de arquivo quando passados como argumentos para os vários comandos do Unix.