У меня есть структура папок, таким образом:
----project\
----datafolder1\
----file1.txt
----file2.txt
----datafolder2\
----file1.txt
----file2.txt
----file1.txt
----file2.txt
В каждом текстовом файле есть строки, содержащие чисто числовые данные (целые и десятичные дроби), а также другую ненужную информацию. К ним относятся:
пробелы для начала строки между двумя интересующими числовыми значениями перед концом строки, например:
<blank><blank>43<blank><tab>73.5<blank><end of line>
Я бы хотел, чтобы это было просто:
43<blank>73.5<end of line>
пустые строки.
Я бы хотел, чтобы эти пустые строки были удалены, чтобы все интересные данные находились на соседних и непрерывных строках.
строки с буквами, например:
---next line contains 50 customer data----
Я хочу, чтобы они тоже были удалены.
Вместо того, чтобы выполнять эти модификации вручную, я хотел бы автоматизировать это с помощью скрипта, который запускается из папки project\
и рекурсивно посещает datafolder1
, а затем datafolder2
, работает с текстовыми файлами, а затем создает измененный текстовый файл с указанными выше свойствами, помеченными modfile1.txt
, modfile2.txt
и так далее.
Рекурсивное посещение подпапок кажется возможным, используя ответ, указанный здесь . Использование grep для поиска только строк, содержащих числа, кажется возможным в соответствии с ответом здесь. Однако это работает только в том случае, если каждая интересующая строка содержит только одно число. В моем случае интересующая строка может содержать несколько целых чисел (положительных или отрицательных) и десятичных дробей, разделенных пробелами. Наконец, собрать все это вместе в сценарий мне не под силу, учитывая мои текущие знания об этих инструментах. Я в порядке, если все это можно сделать в awk
или в .sh
.
Вы можете использовать awk для удаления пустых строк и строк, содержащих буквы, обрезать начальные и конечные пробелы, а также сжимать пробелы между словами.
# selects *.txt minus mod*.txt
find . -name '*.txt' ! -name 'mod*' -exec awk '
FNR == 1 {
close(fn)
fn = FILENAME
sub(/.*//, "&mod", fn)
}
/[[:alpha:]]/ { next }
NF { $1 = $1; print > fn }' {} +
Как работает $1 = $1
, смотрите ответ Эда здесь.
Хотя это можно сделать с помощью awk
, как показано в принятом ответе, это также можно сделать с помощью Perl:
find . -name '*.txt' -exec perl -i.bak -nle '
next unless ( /^[\s\d\.\-]+$/ && /\d/ ); # skip unwanted lines
s/\s+/ /g; # keep only single spaces
s/^\s+|\s+$//g; # trim whitespace at start and end
print' {} +
Это использует -i.bak
для замены на месте, сохраняя исходные файлы с расширением .bak
.
Параметр -l
добавляет новую строку, потому что мы обрезали все пробельные символы с конца (также удаляя символы \r (CR), если файлы были получены из Windows)
Если важно сохранить исходные имена файлов, вы можете сделать что-то подобное впоследствии
find . -name "*.txt.bak" -print0 \
| while IFS= read -r -d '' f; do
mv "${f%%.bak}" "${f%%.txt.bak}-new.txt";
mv "$f" "${f%%.bak}"
done
Вот аналогичная версия с использованием sed.
find -name \*.txt -print -exec sh -c "sed -r '/(^\s*$|[[:alpha:]])/d ; s/\s+/ /g ; s/(^\s|\s$)//g' '{}' > '{}.mod'" \;
Есть небольшая проблема с именованием новых файлов как modfile.txt. В следующий раз, когда вы запустите его, он обработает modfile.txt и создаст modmodfile.txt. Добавление суффикса .mod предотвратит обработку измененных файлов.
/(^\s*$|[[:alpha:]])/d # delete blank lines or lines with alpha
s/\s+/ /g # replace multiple spaces with one space
s/(^\s|\s$)//g # replace space at the beginning or end of the line with nothing
Регулярное выражение, которое я использую для отбрасывания пустых строк (поэтому целая строка, заполненная пробелами и вкладками, даже вариантами Unicode, в этом случае представляет собой пустую строку) просто выполните
mawk1.3.4 'BEGIN { FS = "^$" } /[[:graph:]]/'
FS = "^$", чтобы он не тратил впустую поля ЦП, которые вам не нужны.
Слово предостережения - вместо этого используйте mawk 1.3.
***пс:
Причина, по которой gnu-awk здесь выбита, заключается в том, что, несмотря на то, что gawk и mawk2 совпадают друг с другом на /[[:graph:]]/, некоторые из моих внутренних тестов поняли, что оба будут выбрасывать кучу корейских хангыль и некоторые смайлики в 4 -байтовое пространство юникода.
только mawk1.3.4 правильно их учитывает.
пс2:
FS = "^$" is faster than FS = RS