#string #bash #awk #sed
#строка #bash #awk #sed
Вопрос:
После некоторого преобразования разнородных данных в строку появляются файлы со следующим содержимым:
file1.txt:
mat 445
file2.txt:
mat 734.2
и так далее. Но есть и злоумышленники, которые не соответствуют этому шаблону, например.
filen.txt:
mat 1
FBW
Со всем, что начинается с «mat», я хотел бы продолжить, в то время как все остальные строки должны быть удалены.
Следующее не работает (и кажется довольно громоздким):
for f in *.txt ; do
if [[ ${f:0:3} == "mat" ]]; then
# do some string conversion with that line, which is not important here
sed -i -e 's/^.*(mat.*).*$/1/' $f
sed -i -e 's/ //g' $f
tr '.' '_' < $f
sed -i -e 's/^/<http://uricorn.fly/tib_lok_sys#/' "$f"
sed -i -e 's/(.*)[0-9]/amp;> /' "$f"
else
# delete the line that does not match the pattern
sed -i -e '^[mat]/d' $f
fi
done
Как показано в приведенном ниже комментарии, условие if неверно, поскольку оно соответствует не содержимому файла, а его имени.
Затем желаемый результат должен быть:
file1.txt
<http://uricorn.fly/tib_lok_sys#mat445>
file2.txt
<http://uricorn.fly/tib_lok_sys#mat734_2>
filen.txt
<http://uricorn.fly/tib_lok_sys#mat1>
Как этого можно достичь?
Комментарии:
1. Вы абсолютно правы в том, что встречаются мат 1 e или мат 1 (2), но я подумал, что смогу разобраться с этим позже и, надеюсь, самостоятельно.
Ответ №1:
Исходные данные, с некоторыми дополнениями, добавленными к последним 2 файлам:
$ for s in 1 2 n
do
fn="file${s}.txt"
echo " ${fn}"
cat "${fn}"
done
file1.txt
mat 445
file2.txt
mat 734.2.3
filen.txt
mat 1 2 3
FBW
Одно awk
из решений, которое реализует самый последний набор изменений вопроса:
awk -i inplace ' # overwrite the source file
/^mat/ { gsub(/ /,"") # if line starts with "^mat" then remove spaces ...
gsub(/./,"_") # and replace periods with underscores
printf "<http://uricorn.fly/tib_lok_sys#%s>n", $0 # print the desired output
}
' file{1,2,n}.txt
Примечания:
- для этого
-i inplace
параметра требуетсяGNU awk 4.1.0
(или лучше) - удалите комментарии к коду declutter
Приведенное выше генерирует следующее:
$ for s in 1 2 n
do
fn="file${s}.txt"
echo " ${fn}"
cat "${fn}"
done
file1.txt
<http://uricorn.fly/tib_lok_sys#mat445>
file2.txt
<http://uricorn.fly/tib_lok_sys#mat734_2_3>
filen.txt
<http://uricorn.fly/tib_lok_sys#mat123>
Ответ №2:
Sed:
sed -ri '/^mat/{s/[ ]//g;s/[.]/_/g;s@^(.*)$@<http://uricorn.fly/tib_lok_sys#1>@g}' *.txt
Найдите строки, начинающиеся с mat, а затем сначала удалите пробелы, замените. с помощью _ и, наконец, замените эту строку строкой, включающей добавленную строку http.
Комментарии:
1. Это почти идеально. К сожалению, эта версия не заменяет исходную строку, а записывает ее дополнительно.
2. Вы имеете в виду, что это не изменяет исходный файл?
3. Попробуйте альтернативу sed
4. Команда awk создает строку, но не преобразует строку в соответствии с командами sed и tr, приведенными в примере выше: для file2.txt точка в строке должна быть преобразована в символ подчеркивания. Итак, awk ‘/^mat/ { print «< uricorn.fly/tib_lok_sys#»$0 «>» }’ $ f > $g создает < uricorn.fly/tib_lok_sys#mat734.2 > вместо < uricorn.fly/tib_lok_sys#mat734_2 >
5. ОК. решение sed было изменено
Ответ №3:
Другие ответы гораздо более изящны, но ни один из них не работал в моей системе, так что вот что в итоге получилось:
for f in *.txt ; do
# Remove every line that does not contain 'mat'
sed -i '/mat/!d' $f
# Remove every character until 'mat' begins
sed -i -e 's/^.*(mat.*).*$/1/' $f
# Remove the blank between 'mat' and number
sed -i -e 's/ //g' $f
# Replace the dot in subcategories with an underscore
tr '.' '_' < $f
# Add URI
sed -i -e 's/^/<http://uricorn.fly/tib_lok_sys#/' "$f"
sed -i -e 's/(.*)[0-9]/amp;> /' "$f"
uniq $f
Выполнено