Por que os comandos cat, grep e outros não conseguem entender os arquivos que começam com o sinal de menos? [duplicado]

5

Se eu tiver um arquivo cujo nome começa com um ou vários sinais de menos, por exemplo --1 , ele não pode ser usado como um parâmetro de muitos comandos. Mesmo se eu correr

cat --1

em vez de conteúdo de arquivo, recebo uma mensagem de erro de opção não reconhecida:

cat: unrecognized option '--1'

O mesmo efeito aparece quando digito

cat "--1"
cat '--1'
cat \-\-1

nada funciona. Os arquivos que começam com '-' não são ilegais no sistema de arquivos, mas é muito difícil trabalhar com eles em cli e scripts. OK, no caso de cat ou grep , posso usar

cat <--1

e isso funcionará, mas

rm --1

também não funciona e é muito difícil substituir esse comando por outra coisa. Muito desconfortável depois de tudo. Existe alguma solução alternativa universal diferente de não usar esses nomes de arquivo?

BTW, se o arquivo de nome for único - e todos os comandos entenderão como stdin , não é?

E seria difícil não usar esses nomes de arquivos, pois eu não posso renomeá-los no script usando o comando mv .

    
por Nick 19.08.2013 / 18:36

4 respostas

18

A convenção é que tudo o que começa com - é uma opção. Isso colide com nomes de arquivos que começam com - . Para contornar essa maioria dos comandos, reconheça -- como sentinela de fim de opções. Tudo após o -- não será reconhecido como opções, em vez disso, serão considerados literalmente como nomes de arquivos.

Por exemplo

cat -- --1

ou

rm -rf -- --1

Outro método é qualificar o nome do arquivo com um caminho.

por exemplo

cat ./--1

ou até mesmo

cat /home/username/foo/--1

O melhor é nunca criar nomes de arquivos que comecem com - . Os nomes dos arquivos podem conter todos os tipos de caracteres, incluindo a nova linha, mas somente porque é possível, não significa que você tenha que fazê-lo.

Para responder à sua pergunta: Por que os comandos cat, grep e outros não conseguem entender os arquivos que começam com o sinal de menos? Porque a linha de comando é apenas uma string. O shell faz alguma expansão de nome de arquivo e alguma divisão de palavras e alguma substituição de variável, mas no final o programa recebe um array de strings.

Como você separa as strings que significam opções das strings que significam nomes de arquivos? Por alguns personagens sentinelas. A convenção no mundo unix / linux é usar o - como o caractere sentinela. Tudo o que começa com - é uma opção. Bem, quase tudo. Os argumentos para as opções também podem começar com - , mas esse é o detalhe do programa individual.

Nem todo programa está em conformidade com esta convenção. Indiscutivelmente o exemplo mais popular é dd :

dd if=/dev/random of=whatfreespace

dd reconhece nomes de arquivos porque aparecem após o sinal = .

A maioria, se não todos, os programas GNU estão de acordo com a convenção GNU getopt: opções curtas começam com - e são apenas um caractere, as opções longas começam com -- e são pelo menos dois caracteres. -- sozinhos marca o final das opções e todas as strings depois dessa marca não são reconhecidas como opções. Para mais detalhes, leia Convenções de Sintaxe do Argumento do Programa na GNU libc manual.

Um exemplo de um programa que está parcialmente de acordo com a convenção GNU é find :

find -not -name porn -delete

Embora find use - para diferir de opções de não opções, elas não diferem de opções longas de opções curtas. Ele reconhece -- como o final das opções. Cada programa pode definir sua própria maneira de reconhecer opções e nomes de arquivos e qualquer outra coisa.

A convenção para interpretar um único - como stdin também é apenas isso, uma convenção de como interpretar o único caractere - quando ele é encontrado na matriz de cadeias de caracteres.

    
por 19.08.2013 / 18:39
5

Qualquer coisa que comece com um traço é considerada uma opção. Você pode preceder o caminho para o nome do arquivo para que ele não pareça uma opção:

cat ./--filename
    
por 19.08.2013 / 18:48
3

Usar cat -- --filename indica parar de procurar por qualquer opção. Há um problema em potencial com essa abordagem, pois os aplicativos de terceiros não sabem como lidar com nomes de arquivos com tracejado. No entanto, binários POSIX como cp, rm, cat etc sabem como lidar com esses traços

    
por 19.08.2013 / 18:45
1

Mais geralmente, é:

cat $options_and_or_file_patterns_or_stdin_if_dash

cat "$option_or_file_or_stdin_if_dash"

cat -- "$file_or_stdin_if_dash"

cat < "$file"

cat - < "$file" # same as above
    
por 19.08.2013 / 20:07