Bash: как проверить наличие первых трех символов в файле

#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
 

Выполнено