У меня есть текстовый файл, содержащий одно имя файла в каждой строке:
111_c4l5r120.png
123_c4l4r60.png
135_c4l4r180.png
147_c4l3r60.png
15_c4l1r120.png
...
Я хочу преобразовать его в такую форму:
111_c4l5r120.png 111
123_c4l4r60.png 123
135_c4l4r180.png 135
147_c4l3r60.png 147
15_c4l1r120.png 15
...
используя этот код:
#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
echo "$line" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt
done < "$1"
но результат таков:
111_c4l5r120.png
111
123_c4l4r60.png
123
135_c4l4r180.png
135
147_c4l3r60.png
147
15_c4l1r120.png
15
...
Как мне изменить свой сценарий, чтобы получить желаемое на выходе?
решение1
Не делайте такого рода вещи в оболочке! Это гораздо сложнее, чем необходимо, подвержено ошибкам и гораздо, гораздо, медленнее. Существует множество инструментов, предназначенных для таких текстовых манипуляций. Например, в sed
(здесь предполагается недавняя реализация GNU или BSD для -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
Или, для любого 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 -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
решение2
Если у вас нет особой необходимости использовать оболочку для этого,ответ тердонапредоставляет лучшие альтернативы.
Поскольку вы используете bash
(как указано в скрипте), вы можете использовать -n
опцию echo:
echo -n "${line} " >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt
Или вы можете использовать функции оболочки для обработки строки без использования cut
:
echo "${line} ${line%%_*}" >> output.txt
(замена обеих echo
строк).
В качестве альтернативы, printf
тоже подойдет, работает в любомОболочка POSIX, и в целом лучше (см.Почему printf лучше, чем echo?для получения подробной информации):
printf "%s " "${line}" >> output.txt
echo "$line" | cut -d'_' -f 1 >> output.txt
или
printf "%s %s\n" "${line}" "${line%%_*}" >> output.txt
(Строго говоря, на простом языке /bin/sh
,echo -n
не является портативным. Поскольку вы явно используете bash
его, здесь все в порядке.)
решение3
Вот, пожалуйста:
#!/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"
Выход:
$ 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