:é 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.
:>não é um único operador. Pode ser mais fácil entender se você o ler como: > filealternativa.