Программирование Awk: разделение большого файла на более мелкие на основе шаблона

cd ~

Изменения в вашем домашнем каталоге. ~ в начале пути является аббревиатурой, означающей «домашний каталог пользователя».

cd /

Изменяет корневой каталог /. Здесь нет ничего особенного.

cd ~/

Конечная / не имеет никакого значения. Он заставляет ~ интерпретироваться как каталог, но cd делает это в любом случае. (Конечная / имеет значение для символической ссылки на каталог - сравните ls -ld /var/spool/mail и ls -ld /var/spool/mail/.)

cd -

Изменения в каталоге, в котором вы были до предыдущего . Это особый случай команды cd: когда его аргумент -, он делает это.

cd --

С большинством команд, включая cd, аргумент -- означает, что все, что приходит после этого, будет рассматриваться как операнд, а не вариант. Так, например, cd -- -P означает изменение в каталог с именем -P, тогда как cd -P передает параметр -P (что имеет значение, если путь, по которому вы переходите, проходит через символическую ссылку). Если после -- нет аргументов, -- ничего не делает; эта команда эквивалентна простой cd. cd без аргумента, в свою очередь, является ярлыком для cd ~.

cd /.

Эквивалентен cd /, поскольку /. - это тот же каталог, что и / (. в основном полезен сам по себе, означает «текущий каталог»).

cd $HOME

Изменения в вашем домашнем каталоге. Это не удается, если путь к вашему домашнему каталогу содержит пробел или другие символы. Всегда используйте двойные кавычки вокруг переменных подстановок: cd "$HOME".

cd $USR

По всей вероятности, это ничего не значит, потому что в вашей оболочке не определена переменная с именем USR, поэтому выполняемая команда просто [ f37].

4
задан 19 May 2017 в 11:24

6 ответов

Это можно сделать полностью в awk:

$ awk '$1 ~ /kpoint[0-9]/ { file = $1 ".dat" } {print > file}' file
$ head kpoint*
==> kpoint1.dat <==
kpoint1 :       0.0000    0.0000    0.0000
  band No.  band energies     occupation
      1     -52.8287      2.00000
      2     -52.7981      2.00000
      3     -52.7981      2.00000

==> kpoint2.dat <==
 kpoint2 :       0.0000    0.0000    0.0000
  band No.  band energies     occupation
      1     -52.8287      2.00000
      2     -52.7981      2.00000
      3     -52.7981      2.00000

Awk также поддерживает > file для перенаправления с некоторыми незначительными отличиями (см. руководство GNU awk для более).

3
ответ дан 22 May 2018 в 22:29
  • 1
    Это сработало. Большой!! благодаря – Sruthil Lal S.B. 19 May 2017 в 11:56
  • 2
    @ SruthilLalS.B. Привет, если этот ответ будет работать для вас, нажмите на галочку, чтобы принять его. – P.-H. Lin 19 May 2017 в 12:10

Это можно сделать полностью в awk:

$ awk '$1 ~ /kpoint[0-9]/ { file = $1 ".dat" } {print > file}' file $ head kpoint* ==> kpoint1.dat <== kpoint1 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000 ==> kpoint2.dat <== kpoint2 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000

Awk также поддерживает > file для перенаправления с некоторыми незначительными отличиями (см. руководство GNU awk для более).

3
ответ дан 18 July 2018 в 13:04

Это можно сделать полностью в awk:

$ awk '$1 ~ /kpoint[0-9]/ { file = $1 ".dat" } {print > file}' file $ head kpoint* ==> kpoint1.dat <== kpoint1 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000 ==> kpoint2.dat <== kpoint2 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000

Awk также поддерживает > file для перенаправления с некоторыми незначительными отличиями (см. руководство GNU awk для более).

3
ответ дан 24 July 2018 в 20:06

В то время как ответ муру является самым простым, существует несколько других способов без использования awk.

Perl

Подход с awk в основном заключается в том, что мы пишем конкретное имя файла и измените это имя файла, если и только если мы встретим kpoint в начале строки. Такой же подход можно сделать с помощью Perl:

$ perl -ane '$p=$F[0] if $F[0] =~ /kpoint/;open($f,">>",$p . ".dat"); print $f $_' input.txt

Вот как это работает:

-a флаг позволяет нам использовать специальный @F массив слов, которые были автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk $p=$F[0] if $F[0] =~ /kpoint/ предназначен для изменения $p (который предназначен для префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение соответствия этого шаблона может быть /^ *kpoint/ на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с строкой .dat; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как:
$ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt
И, наконец, print $f $_ просто печатает любое открытое имя файла.

split

Из вашего примера видно, что каждая запись состоит из 5 строк. Если это константа, мы можем разбить файл таким образом, не полагаясь на сопоставление шаблонов с split. В частности, эта команда:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt  kpoint

В этой опции команды следующие:

-a флаг позволяет нам использовать специальный @F массив слов, которые имеют автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk --numeric-suffixes=1 позволит нам добавлять меняющиеся числа, начиная с 1 до каждого имени файла $p=$F[0] if $F[0] =~ /kpoint/, для изменения $p ( который должен быть префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение этого совпадения шаблонов может быть /^ *kpoint/ input.txt - это файл, который мы пытаемся разделить на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с .dat строка; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как:
$ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt

И вот как это работает на практике:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt  kpoint                                                                        
$ cat kpoint01.dat                                                                                                                                    
kpoint1 :       0.0000    0.0000    0.0000
  band No.  band energies     occupation 
      1     -52.8287      2.00000
      2     -52.7981      2.00000
      3     -52.7981      2.00000
$ cat kpoint02.dat                                                                                                                                    
 kpoint2 :       0.0000    0.0000    0.0000
  band No.  band energies     occupation 
      1     -52.8287      2.00000
      2     -52.7981      2.00000
      3     -52.7981      2.00000

Кроме того, мы могли бы добавить --suffix-length=1, чтобы длина каждого числового суффикса была короче, чем kpoint1, а не kpoint01, но это может быть проблемой, если у вас есть большое количество kpoint s.

alternative awk

Это похоже на ответ muru , за исключением того, что мы используем различное совпадение шаблонов, а также другой подход к созданию переменной имени файла через sprintf()

$ awk '/^\ *kpoint/{f=sprintf("%s.dat",$1)};{print > f}' input.txt

Python

Хотя подходы awk и split короче, другие инструменты, такие как Python, хорошо подходят для обработки текста, и мы можем использовать их для реализации большего подробные, но рабочие решения.

Сценарий ниже делает именно это, и он работает над идеей оглянуться назад в список строк, которые мы сохраняем. Сценарий сохраняет строки сохранения до тех пор, пока он не встретит kpoint в начале строки, а это значит, что мы достигли новой записи, что также означает, что нам нужно записать предыдущую запись в соответствующий файл.

#!/usr/bin/env python3
import sys

def write_entry(pref,line_list):
    # this function writes the actual file for each entry
    with open(".".join([pref,"dat"]),"w") as entry_file:
        entry_file.write("".join(line_list))

def main():
    prefix = ""
    old_prefix = ""
    entry=[]
    with open(sys.argv[1]) as fd:
        for line in fd:
            # if we encounter kpoint string, that's a signal
            # that we need to write out the list of things 
            if line.strip().startswith('kpoint'):
                prefix=line.strip().split()[0]
                # This if statement counters special case
                # when we just started reading the file
                if not old_prefix:
                    old_prefix = prefix
                    entry.append(line)
                    continue
                write_entry(old_prefix,entry)
                old_prefix = prefix
                entry=[]
            # Keep storing lines. This works nicely after old 
            # entry has been cleared out. 
            entry.append(line)
    # since we're looking backwards, we need one last call
    # to write last entry when input file has been closed
    write_entry(old_prefix,entry)

if __name__ == '__main__': main()

Pure Bash

Почти та же идея, что и подход Perl - мы продолжаем записывать все в определенное имя файла и изменять имя файла только тогда, когда находим строку с kpoint.

#!/usr/bin/env bash

while IFS= read -r line;
do
    case "$line" in
        # We found next entry. Use word-splitting to get
        # filename into fname variable, and truncate that filename
        *kpoint[0-9]*) read fname trash <<< $line  && 
                       echo "$line" > "$fname".dat ;;
        # That's just a line within entry. Append to 
        # current working file
        *) echo "$line" >> "$fname".dat ;;
    esac
done < "$1"

# Just in case there are trailing lines that weren't processed
# in while loop, append them to last filename
[ -n "$line" ] && echo "$line" >> "$fname".dat ;
2
ответ дан 22 May 2018 в 22:29

Хотя ответ муру является самым простым, существует несколько других способов без использования awk.

Perl

Подход с awk в основном заключается в том, что мы пишем конкретное имя файла и измените это имя файла, если и только если мы встретим kpoint в начале строки. Такой же подход можно сделать с помощью Perl:

$ perl -ane '$p=$F[0] if $F[0] =~ /kpoint/;open($f,">>",$p . ".dat"); print $f $_' input.txt

Вот как это работает:

-a флаг позволяет нам использовать специальный @F массив слов, которые были автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk $p=$F[0] if $F[0] =~ /kpoint/ предназначен для изменения $p (который предназначен для префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение соответствия этого шаблона может быть /^ *kpoint/ на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с строкой .dat; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как: $ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt И, наконец, print $f $_ просто печатает любое открытое имя файла.

split

Из вашего примера видно, что каждая запись состоит из 5 строк. Если это константа, мы можем разбить файл таким образом, не полагаясь на сопоставление шаблонов с split. В частности, эта команда:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint

В этой опции команды следующие:

-a флаг позволяет нам использовать специальный @F массив слов, которые имеют автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk --numeric-suffixes=1 позволит нам добавлять меняющиеся числа, начиная с 1 до каждого имени файла $p=$F[0] if $F[0] =~ /kpoint/, для изменения $p ( который должен быть префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение этого совпадения шаблонов может быть /^ *kpoint/ input.txt - это файл, который мы пытаемся разделить на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с .dat строка; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как: $ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt

И вот как это работает на практике:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint $ cat kpoint01.dat kpoint1 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000 $ cat kpoint02.dat kpoint2 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000

Кроме того, мы могли бы добавить --suffix-length=1, чтобы длина каждого числового суффикса была короче, чем kpoint1, а не kpoint01, но это может быть проблемой, если у вас есть большое количество kpoint s.

alternative awk

Это похоже на ответ muru , за исключением того, что мы используем различное совпадение шаблонов, а также другой подход к созданию переменной имени файла через sprintf()

$ awk '/^\ *kpoint/{f=sprintf("%s.dat",$1)};{print > f}' input.txt

Python

Хотя подходы awk и split короче, другие инструменты, такие как Python, хорошо подходят для обработки текста, и мы можем использовать их для реализации большего подробные, но рабочие решения.

Сценарий ниже делает именно это, и он работает над идеей оглянуться назад в список строк, которые мы сохраняем. Сценарий сохраняет строки сохранения до тех пор, пока он не встретит kpoint в начале строки, а это значит, что мы достигли новой записи, что также означает, что нам нужно записать предыдущую запись в соответствующий файл.

#!/usr/bin/env python3 import sys def write_entry(pref,line_list): # this function writes the actual file for each entry with open(".".join([pref,"dat"]),"w") as entry_file: entry_file.write("".join(line_list)) def main(): prefix = "" old_prefix = "" entry=[] with open(sys.argv[1]) as fd: for line in fd: # if we encounter kpoint string, that's a signal # that we need to write out the list of things if line.strip().startswith('kpoint'): prefix=line.strip().split()[0] # This if statement counters special case # when we just started reading the file if not old_prefix: old_prefix = prefix entry.append(line) continue write_entry(old_prefix,entry) old_prefix = prefix entry=[] # Keep storing lines. This works nicely after old # entry has been cleared out. entry.append(line) # since we're looking backwards, we need one last call # to write last entry when input file has been closed write_entry(old_prefix,entry) if __name__ == '__main__': main()

Pure Bash

Почти та же идея, что и подход Perl - мы продолжаем записывать все в определенное имя файла и изменять имя файла только тогда, когда находим строку с kpoint.

#!/usr/bin/env bash while IFS= read -r line; do case "$line" in # We found next entry. Use word-splitting to get # filename into fname variable, and truncate that filename *kpoint[0-9]*) read fname trash <<< $line && echo "$line" > "$fname".dat ;; # That's just a line within entry. Append to # current working file *) echo "$line" >> "$fname".dat ;; esac done < "$1" # Just in case there are trailing lines that weren't processed # in while loop, append them to last filename [ -n "$line" ] && echo "$line" >> "$fname".dat ;
2
ответ дан 18 July 2018 в 13:04

Хотя ответ муру является самым простым, существует несколько других способов без использования awk.

Perl

Подход с awk в основном заключается в том, что мы пишем конкретное имя файла и измените это имя файла, если и только если мы встретим kpoint в начале строки. Такой же подход можно сделать с помощью Perl:

$ perl -ane '$p=$F[0] if $F[0] =~ /kpoint/;open($f,">>",$p . ".dat"); print $f $_' input.txt

Вот как это работает:

-a флаг позволяет нам использовать специальный @F массив слов, которые были автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk $p=$F[0] if $F[0] =~ /kpoint/ предназначен для изменения $p (который предназначен для префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение соответствия этого шаблона может быть /^ *kpoint/ на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с строкой .dat; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как: $ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt И, наконец, print $f $_ просто печатает любое открытое имя файла.

split

Из вашего примера видно, что каждая запись состоит из 5 строк. Если это константа, мы можем разбить файл таким образом, не полагаясь на сопоставление шаблонов с split. В частности, эта команда:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint

В этой опции команды следующие:

-a флаг позволяет нам использовать специальный @F массив слов, которые имеют автоматически отделяется от каждой строки входного файла. Таким образом, $F[0] относится к первому слову, так же как $1 в awk --numeric-suffixes=1 позволит нам добавлять меняющиеся числа, начиная с 1 до каждого имени файла $p=$F[0] if $F[0] =~ /kpoint/, для изменения $p ( который должен быть префиксной переменной) тогда и только тогда, когда kpoint находится в строке. Улучшение этого совпадения шаблонов может быть /^ *kpoint/ input.txt - это файл, который мы пытаемся разделить на каждой итерации, которую мы открываем для добавления файла с именем $p, соединенным с .dat строка; обратите внимание, что добавление части важно. Если вы хотите иметь четкий пробег, вы, вероятно, захотите избавиться от старых файлов kpoint. Если мы хотим, чтобы файл всегда был создан свежим и перезаписанным, мы можем перезаписать исходную команду как: $ perl -ane 'if ($F[0] =~ /kpoint/){$p=$F[0]; open($f,">",$p . ".dat")}; print $f $_' input.txt

И вот как это работает на практике:

$ split --additional-suffix=".dat" --numeric-suffixes=1 -l 5 input.txt kpoint $ cat kpoint01.dat kpoint1 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000 $ cat kpoint02.dat kpoint2 : 0.0000 0.0000 0.0000 band No. band energies occupation 1 -52.8287 2.00000 2 -52.7981 2.00000 3 -52.7981 2.00000

Кроме того, мы могли бы добавить --suffix-length=1, чтобы длина каждого числового суффикса была короче, чем kpoint1, а не kpoint01, но это может быть проблемой, если у вас есть большое количество kpoint s.

alternative awk

Это похоже на ответ muru , за исключением того, что мы используем различное совпадение шаблонов, а также другой подход к созданию переменной имени файла через sprintf()

$ awk '/^\ *kpoint/{f=sprintf("%s.dat",$1)};{print > f}' input.txt

Python

Хотя подходы awk и split короче, другие инструменты, такие как Python, хорошо подходят для обработки текста, и мы можем использовать их для реализации большего подробные, но рабочие решения.

Сценарий ниже делает именно это, и он работает над идеей оглянуться назад в список строк, которые мы сохраняем. Сценарий сохраняет строки сохранения до тех пор, пока он не встретит kpoint в начале строки, а это значит, что мы достигли новой записи, что также означает, что нам нужно записать предыдущую запись в соответствующий файл.

#!/usr/bin/env python3 import sys def write_entry(pref,line_list): # this function writes the actual file for each entry with open(".".join([pref,"dat"]),"w") as entry_file: entry_file.write("".join(line_list)) def main(): prefix = "" old_prefix = "" entry=[] with open(sys.argv[1]) as fd: for line in fd: # if we encounter kpoint string, that's a signal # that we need to write out the list of things if line.strip().startswith('kpoint'): prefix=line.strip().split()[0] # This if statement counters special case # when we just started reading the file if not old_prefix: old_prefix = prefix entry.append(line) continue write_entry(old_prefix,entry) old_prefix = prefix entry=[] # Keep storing lines. This works nicely after old # entry has been cleared out. entry.append(line) # since we're looking backwards, we need one last call # to write last entry when input file has been closed write_entry(old_prefix,entry) if __name__ == '__main__': main()

Pure Bash

Почти та же идея, что и подход Perl - мы продолжаем записывать все в определенное имя файла и изменять имя файла только тогда, когда находим строку с kpoint.

#!/usr/bin/env bash while IFS= read -r line; do case "$line" in # We found next entry. Use word-splitting to get # filename into fname variable, and truncate that filename *kpoint[0-9]*) read fname trash <<< $line && echo "$line" > "$fname".dat ;; # That's just a line within entry. Append to # current working file *) echo "$line" >> "$fname".dat ;; esac done < "$1" # Just in case there are trailing lines that weren't processed # in while loop, append them to last filename [ -n "$line" ] && echo "$line" >> "$fname".dat ;
2
ответ дан 24 July 2018 в 20:06

Другие вопросы по тегам:

Похожие вопросы: