Como remover \ n entre as saídas de dois comandos de eco?

13

Eu tenho um arquivo de texto contendo um nome de arquivo em cada linha:

111_c4l5r120.png
123_c4l4r60.png
135_c4l4r180.png
147_c4l3r60.png
15_c4l1r120.png
...

Eu quero convertê-lo nesta forma:

111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
...

usando este código:

#!/bin/bash
while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" >> output.txt   
   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

mas, o resultado é:

111_c4l5r120.png 
111
123_c4l4r60.png 
123
135_c4l4r180.png 
135
147_c4l3r60.png 
147
15_c4l1r120.png 
15
...

Como devo mudar meu script para obter o resultado desejado?

Todos
fonte
O Google encontra melhores resultados, por exemplo, isso .
Thomas Dickey

Respostas:

17

A menos que você tenha uma necessidade específica de usar o shell para isso, a resposta de terdon fornece melhores alternativas.

Como você está usando bash(como indicado no shebang do script), você pode usar a -nopção para ecoar:

echo -n "${line} " >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

Ou você pode usar os recursos de shell para processar a linha sem usar cut:

echo "${line} ${line%%_*}" >> output.txt

(substituindo as duas echolinhas).

Como alternativa, printfisso também funcionaria, funciona em qualquer shell POSIX e geralmente é melhor (consulte Por que o printf é melhor que o eco? Para obter detalhes):

printf "%s " "${line}" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt

ou

printf "%s %s\n" "${line}" "${line%%_*}" >> output.txt

(Estritamente falando, na planície /bin/sh, echo -nnão é portátil . Desde que você está usando explicitamente bashque está tudo bem aqui.)

Stephen Kitt
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
terdon
23

Não faça esse tipo de coisa no shell! É muito mais complexo do que o necessário, propenso a erros e muito, muito, mais lento. Existem muitas ferramentas projetadas para essa manipulação de texto. Por exemplo, em sed(assumindo aqui implementações recentes de GNU ou BSD para -E):

$ sed -E 's/([^_]*).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

Ou, para qualquer sed:

$ sed 's/\([^_]*\).*/& \1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

Perl:

$ perl -pe 's/(.+?)_.*/$& $1/' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15

awk:

$ awk -F_ '{print $0,$1}' file
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
terdon
fonte
1
Utilitários externos não são muito melhores, no entanto.
EKons
6
@ ΈρικΚωνσταντόπουλος sim, eles são. Várias ordens de magnitude mais rápidas, na verdade. O shell simplesmente não é muito bom nesse tipo de coisa. A principal tarefa de um shell é lançar utilitários externos, afinal. Compare o tempo gasto pela abordagem do OP com o tempo gasto por qualquer uma das soluções aqui. Loops de casca são muito, muito lentos. Se você precisar de mais convencimento, leia isto .
terdon
Em termos de portabilidade, não. Em termos de velocidade, sim. Além disso, @ StéphaneChazelas é seu apelido?
EKons
4
@ ΈρικΚωνσταντόπουλος Θα 'θελα :) Não, ele só escreveu duas ótimas respostas que foram relevantes para os dois tópicos de comentários. Quanto à portabilidade, com a exceção (menor) da abordagem perl, que funcionará apenas em algo como ~ 90% das máquinas * nix, todas as três soluções são portáteis e independentes de shell. Ou, ok, você sempre pode criar sedum sed 's/\([^_]*\).*/& \1/' filepara portabilidade extra. O ponto é que você pode contar awke sedestar lá mais do que pode contar com praticamente qualquer outra coisa.
terdon
2

Olha Você aqui:

#!/bin/bash

while IFS='' read -r line || [[  -n "$line"  ]]; do
   echo "$line" `echo "$line" | cut -d'_' -f 1` >> output.txt
#   echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"

Resultado:

$ rm -rf output.txt
$ ./test.sh 1.1; cat output.txt
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
Putnik
fonte