#bash #awk
#bash #awk
Вопрос:
У меня следующая ситуация:
У меня есть текстовый файл, который я пытаюсь выполнить в цикле, чтобы я мог знать, совпадает ли каждая строка с «.mp3», в данном случае которая является этой:
12 Stones.mp3
randomfile.txt
Aclarion.mp3
ransomwebpage.html
Agents Of The Sun.mp3
randomvideo.mp4
Итак, я написал следующий скрипт для его обработки:
while read line || [ -n "$line" ]
do
varline=$(awk '/.mp3/{print "yes";next}{print "no"}')
echo $varline
if [ "$varline" == "yes" ]; then
some-command
else
some-command
fi
done < file.txt
Ожидаемый результат будет:
yes
no
yes
no
yes
no
Вместо этого, кажется, пропускает первую строку, и я получаю следующее:
no
yes
no
yes
no
Комментарии:
1. Ваша
awk
команда использует весь ввод после первого вызоваread
; вашаwhile
выполняется только один раз.
Ответ №1:
Вам действительно не нужен Awk для простого сопоставления с шаблоном, если это все, для чего вы его использовали.
while IFS= read -r line; do
case $line in
*.mp3) some-command;,
*) some-other-command;;
esac
done <file.txt
Если вы все равно используете Awk по другим причинам, зацикливание строк в цикле оболочки неэффективно и очень часто является антишаблоном. На самом деле это не исправляет ситуацию, но, по крайней мере, позволяет избежать выполнения нового экземпляра Awk на каждой итерации:
awk '{ print ($0 ~ /.mp3$/) ? "yes" : no" }' file.txt |
while IFS= read -r whether; do
case $whether in
'yes') some-command ;;
'no') some-other-command;;
esac
done
Если вам также нужно содержимое "$line"
, печать этого также из Awk и чтение двух разных переменных является тривиальным изменением.
Я упростил read
выражение, исходя из предположения, что вы можете убедиться, что ваш входной файл правильно сформирован отдельно. Если вы не можете этого сделать, вам нужно вернуть более сложную защиту от пропуска новой строки в последней строке файла.
Комментарии:
1. Обратите также внимание на исправленное регулярное выражение с обратной косой чертой перед точкой и
$
привязкой к концу строки.2. Хотя я, очевидно, согласен с
looping the lines in a shell loop is inefficient and very often an antipattern
в целом, в этом случае часть предварительной обработки awk не исключает цикл оболочки и оператор case для каждой строки, поэтому IMHO бесполезно. Не забудьте использоватьwhile IFS=
по умолчанию для циклов чтения.3. @EdMorton Спасибо; обновлено. Третьим вариантом было бы выполнение команд с
system()
из скрипта Awk, но это еще одна банка с червями, которую я не хотел открывать.
Ответ №2:
Используйте awk
$ awk '{if ($0 ~ /mp3/) {print "yes"} else {print "no"}}' file.txt
yes
no
yes
no
yes
no
Или более лаконичный:
$ awk '/mp3/{print "yes";next}{print "no"}' file.txt
$ awk '{print (/mp3/ ? "yes" : "no")}' file.txt
Комментарии:
1. Что произойдет с файлом с именем
mp3_list.html
? Возможно, было бы разумно закрепить регулярное выражение в конце и проверить наличие явного расширения, например($0 ~ /[.]mp3$/)
.
Ответ №3:
Вы что-то забыли? У вашего awk
нет явного ввода, измените на это вместо этого:
while IFS= read -r read line || [ -n "$line" ]
do
varline=$(echo "$line" | awk '/.mp3/{print "yes";next}{print "no"}')
echo $varline
if [ "$varline" == "yes" ]; then
some-command
else
some-other-command
fi
done < file.txt
В этом случае вам может потребоваться изменить на /.mp3$/
или /.mp3[[:space:]]*$/
для точного соответствия.
Потому что .
будет соответствовать любому символу, так что, например, /.mp3/
тоже будет соответствовать Exmp3but.mp4
.
Обновление: изменено while read line
на while IFS= read -r read line
, чтобы сохранить содержимое каждой строки нетронутым при присвоении переменной.
И awk
часть может быть улучшена до:
awk '{print $0~/.mp3$/ ? "yes":"no"}'
Итак, с awk
only вы можете сделать это следующим образом:
awk '{print $0~/.mp3$/ ? "yes":"no"}' file.txt
Или, если ваша цель — просто команды в структуре if, вы можете просто сделать это:
awk '/.mp3$/{system("some-command");next}{system("some-other-command");}' file.txt
или это:
awk '{system($0~/.mp3$/ ? "some-command" : "some-other-command")}' file.txt