Comment supprimer \ n entre les sorties de deux commandes d'écho?

13

J'ai un fichier texte contenant un nom de fichier dans chaque ligne:

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

Je veux le convertir sous cette forme:

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

en utilisant ce code:

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

mais le résultat est:

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

Comment dois-je changer mon script pour avoir la sortie désirée?

Ali
la source
Google trouve de meilleurs résultats, par exemple, ceci .
Thomas Dickey

Réponses:

17

Sauf si vous avez un besoin spécifique d'utiliser le shell pour cela, la réponse de terdon fournit de meilleures alternatives.

Puisque vous utilisez bash(comme indiqué dans le shebang du script), vous pouvez utiliser l' -noption pour faire écho:

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

Ou vous pouvez utiliser les fonctionnalités du shell pour traiter la ligne sans utiliser cut:

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

(en remplaçant les deux echolignes).

Alternativement, printfferait aussi l'affaire, fonctionne dans n'importe quel shell POSIX et est généralement meilleur (voir Pourquoi printf est-il meilleur qu'echo? Pour plus de détails):

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

ou

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

(À proprement parler, en clair /bin/sh, ce echo -nn'est pas portable . Puisque vous utilisez explicitement, bashc'est OK ici.)

Stephen Kitt
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
terdon
23

Ne faites pas ce genre de choses dans la coquille! Il est beaucoup plus complexe que nécessaire, sujet aux erreurs et de loin, beaucoup plus lent. Il existe de nombreux outils conçus pour une telle manipulation de texte. Par exemple, dans sed(en supposant ici des implémentations GNU ou BSD récentes pour -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, pour tout 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
la source
1
Les utilitaires externes ne sont pas beaucoup mieux, cependant.
EKons
6
@ ΈρικΚωνσταντόπουλος oui ils le sont. Plusieurs ordres de grandeur plus rapidement, en fait. La coque n'est tout simplement pas très bonne dans ce genre de choses. Le travail principal d'un shell est de lancer des utilitaires externes, après tout. Comparez le temps pris par l'approche du PO à celui pris par l'une des solutions ici. Les boucles de coque sont très, très lentes. Si vous avez besoin de plus de conviction, lisez ceci .
terdon
En termes de portabilité, non. En termes de vitesse, oui. Aussi, @ StéphaneChazelas est-il votre alias?
EKons
4
@ ΈρικΚωνσταντόπουλος Θα 'θελα :) Non, il se trouve qu'il a juste écrit 2 bonnes réponses qui étaient pertinentes pour les deux fils de commentaires. En ce qui concerne la portabilité, à l'exception (mineure) de l'approche perl qui ne fonctionnera que sur quelque chose comme ~ 90% des machines * nix, les trois solutions sont portables et indépendantes du shell. Ou, OK, vous pouvez toujours en faire sedun sed 's/\([^_]*\).*/& \1/' filepour une portabilité supplémentaire. Le fait est que vous pouvez compter awket sedêtre là plus que vous ne pouvez compter sur à peu près n'importe quoi d'autre.
terdon
2

Vous voilà:

#!/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"

Production:

$ 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
la source