O que esse estranho símbolo ":>" no bash significa

47

Encontrei algo em um script, mas não pertencendo ao script principal. Havia :>uma fila.

Você poderia me explicar o que isso significa?

:> file
while read A B C D E; do echo "$A;$B;$D;$E;$C" >> file; done < otherfile
diego9403
fonte
6
Importante, :>não é um único operador. Pode ser mais fácil entender se você o ler como : > filealternativa.
jpfx1342
Isso significa que a pessoa que escreve o roteiro deveria ter redirecionado a saída do loop para o arquivo: while read A B C D E; do echo "$A;$B;$D;$E;$C"; done < otherfile > file. Ou melhor ainda, eles deveriam ter usado a ferramenta certa para o trabalho, awk, como sugerido por Peter . Como um aparte, você quase sempre deseja usar o -rswitchread .
Tom Fenech 02/09
Fora da festança, seria um sorriso para um corvo.
smci

Respostas:

46

Havia:> em uma linha de um script bash. O que isso significa?

:> file

É uma maneira curta de dizer:

  • Se filenão existir, crie-o ou trunque-o em 0bytes.

Isso significa que você pode ter certeza de que fileexiste e está vazio.

Você também pode usar, > filemas :> fileé mais portátil.

Veja a pergunta Stack Overflow Qual é o objetivo do ':' (dois pontos) GNU Bash Builtin? Para maiores informações.

DavidPostill
fonte
Eu não entendo a segunda linha. Eu pensei que leu variáveis ​​de leitura. O eco do comando também é estranho. Você poderia explicar?
Diego9403
Eu não sou um especialista em Unix, mas acho que a segunda linha lê as coisas otherfilee echoas envia para file. Ele também cria variáveis ​​do que quer que seja lido ... Se você quiser uma resposta definitiva, faça sua própria pergunta.
DavidPostill
2
@ diego9403: readobtém informações de stdin. Por si só, ele lê o que você digita. Como o stdin foi redirecionado para <otherfile, o conteúdo de otherfileé "digitado" no stdin. Portanto, readobtém os valores linha por linha nas variáveis ​​$ A, $ B, $ C, $ D e $ E.
Slebetman 01/09/2015
Portanto, é apenas uma alternativa mais obscura truncatedo coreutils?
Federico Poloni
11
@ PeterCordes Eu não quis dizer "obscuro" como em "é incomum", mas como em "é menos claro para o leitor".
Federico Poloni
29

Parece uma maneira elegante de criar um novo arquivo. In bash :é um comando nulo:

$ type : 
: is a shell builtin 
$ help : 
:: :
    Null command.

    No effect; the command does nothing.

    Exit Status:
    Always succeeds.

>redireciona a saída de :para um arquivo.

Arkadiusz Drabczyk
fonte
2
Ele também truncará o arquivo, se ele já existir ...
DavidPostill
2
sim, é isso que >faz #
Arkadiusz Drabczyk
2
:é uma abreviação de true. Possivelmente em algumas conchas, truenão está embutido? Ambos são integrados no bash.
Peter Cordes
12

:é outro nome para true. Ambos são shell embutidos no bash, mas não há /bin/:, apenas um /bin/true. O redirecionamento de saída causa o shell open(2)no arquivo O_CREAT|O_TRUNC. Se nada for escrito, ele permanecerá no comprimento zero.

Juntar essas duas partes :> fileé um idioma bastante comum para truncar arquivos. A maioria das pessoas tentaria torná-lo menos esquisito escrevendo : >file.


Como você perguntou em um comentário sobre a segunda linha, vou transformar meus comentários em uma resposta. (mesmo que você não tenha feito isso na sua pergunta.)

A segunda linha é um loop que lê linhas de otherfiledentro de algumas variáveis ​​nomeadas. O corpo do loop usa echopara imprimi-los com ;separadores em vez do espaço em branco que eles tinham antes. fileé fechado e reaberto (para acrescentar) a cada iteração, porque o redirecionamento está dentro do loop. Usar while ...;do read -r ...;done <otherfile >filesugaria menos e evitaria a necessidade de truncar o arquivo primeiro. read -rnão come \como um personagem de fuga.

O processamento de texto no bash é bastante lento. Parte disso é inevitável: readé preciso ir um byte de cada vez (uma read(2)chamada de sistema por byte) para evitar ultrapassar o final de uma linha. Seria melhor usar a ferramenta certa para o trabalho:

awk -vOFS=';' '{ print $1, $2, $4, $5, $3 }' -- otherfile  >file

--significa que seu script não será interrompido se otherfilefor nomeado algo parvo --version.

Definir o separador de campos de saída como ;significa que você pode simplesmente passar vários campos como args para imprimir. O Shell readatribui o restante da linha com espaço em branco à última variável, mas não há como dizer ao awk que só se divida em 5. Se isso for importante, talvez continue usando um loop bash, porque é inconveniente no awk. O Perl facilita isso, pois splitpode levar um argumento de max-fields, mas é muito mais lento iniciar do que o awk.

Na verdade, acabou não sendo tão difícil, apenas um regex feio para escrever. Para obter o restante da linha em vez do $5awk, o loop pelos campos ainda perde o espaço em branco original. Minha primeira idéia viável é usar gensubon $0(a linha inteira) para remover os 4 primeiros campos (ou seja, não espaço seguido de espaço), deixando todo o resto:

awk -vOFS=';' '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1); print $1, $2, $4, tail, $3 }' -- otherfile >file

Eu entendi direito na primeira tentativa, mas o fato de eu estar impressionado comigo mesmo por isso diz algo sobre a legibilidade desse código awk. >. <

Observe como é o mesmo de printantes, mas com tailno lugar de $5.

echo 'A  B c DD    e      f g    f' | 
  awk -vOFS=\; '{ tail = gensub("[[:space:]]*([^[:space:]]+[[:space:]]+){4}", "", 1);
   print $1, $2, $4, tail, $3 }'

A;B;DD;e       f g    f;c

Isso seria mais impressionante se eu pudesse copiar / colar o literal e mostrar que ele apareceu na saída. Digite um no bash com ^ Q. ctrl-Q significa Citar o próximo pressionamento de tecla como um caractere literal, pois a edição de linha no estilo emacs do bash é igual à emacs real para isso.

O http://mywiki.wooledge.org/BashFAQ possui algumas informações úteis sobre scripts de maneiras que não quebram, independentemente dos dados ou nomes de arquivos que você lança no script.

Peter Cordes
fonte