:
é 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 otherfile
dentro de algumas variáveis nomeadas. O corpo do loop usa echo
para 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 >file
sugaria menos e evitaria a necessidade de truncar o arquivo primeiro. read -r
nã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 otherfile
for 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 read
atribui 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 split
pode 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 $5
awk, o loop pelos campos ainda perde o espaço em branco original. Minha primeira idéia viável é usar gensub
on $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 print
antes, mas com tail
no 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.
:>
não é um único operador. Pode ser mais fácil entender se você o ler como: > file
alternativa.