Я совершенно не знаком с сценариями bash. У меня есть текстовый файл (назовите его geom.txt), который я использую, который выглядит следующим образом:

2
C -0.6165934086165 -0.35599823933468 0.00000000 0.8466
C 0.6165934086165 0.35599823933468 0.00000000 0.8466
lattice_vector 2.46670923 0.00000000 0.00000000
lattice_vector 1.23286602 2.13599798 0.00000000
lattice_vector 0.00000000 0.00000000 40

Я хотел бы написать сценарий, который добавит строку между строками 1 и 2, чтобы получить такой результат:

2

C -0.6165934086165 -0.35599823933468 0.00000000 0.8466
C 0.6165934086165 0.35599823933468 0.00000000 0.8466
lattice_vector 2.46670923 0.00000000 0.00000000
lattice_vector 1.23286602 2.13599798 0.00000000
lattice_vector 0.00000000 0.00000000 40

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

2
user3806522 5 Июл 2014 в 01:00

3 ответа

Лучший ответ

Используя gnu sed:

sed -i.bak '2s/^/\n/' file

Использование awk:

awk 'NR==2{print "\n" $0; next}1' file

Использование non-gnu sed

sed -i.bak $'2s/^/\\\n/' file

ИЛИ еще:

eol=$'\n'
sed -i.bak "2s/^/\\$eol/" file

Наконец, вот чистый способ BASH:

c=0
while IFS= read -r line; do
    (( c++ == 1 )) && echo
    echo "$line"
done < file
4
anubhava 5 Июл 2014 в 08:18
Вероятно, это было для того, чтобы предложить более одного решения. :P
 – 
jaypal singh
5 Июл 2014 в 02:26
1
Не допускает ли SO более одного решения в одном ответе? Я думаю, что он не хочет болтать с ОП. Так что только он выложил решения sed для разных типов sed. И +1 за это предварительное мышление.
 – 
Avinash Raj
5 Июл 2014 в 04:12
1
Спасибо @AvinashRaj, приятель Джайпал говорил, что в более легком ключе нет правила, согласно которому нельзя использовать более одного решения в одном ответе :)
 – 
anubhava
5 Июл 2014 в 07:16
1
+1, но стоит отметить, что зацикливание строк входного файла в чистом коде оболочки редко является правильным решением по соображениям производительности. При этом [[ $c == 1 ]] && echo ... ... ((c++)) можно упростить до (( c++ == 1 )) && echo.
 – 
mklement0
5 Июл 2014 в 07:29

Вы можете использовать vim для достижения своей цели.

vim -c 1 -c "normal o" -c wq geom.txt 
0
R Sahu 5 Июл 2014 в 09:40

Или вы можете сделать это в простом цикле while:

let count=0   # set a simple integer to test
:> tmpfile    # create or truncate tmpfile

while read line || test -n "$line"; do
    echo "$line" >> tmpfile   # write each line to tmpfile
    # if this is the first line, add a newline and increment counter
    test "$count" -eq 0 && echo "" >> tmpfile && ((count+=1))
done <"$1"

# when all lines written to tmpfile copy tmpfile to filename given as arg, remove tmpfile
cp tmpfile "$1" && rm -f tmpfile
0
David C. Rankin 5 Июл 2014 в 17:43
Нет никакого преимущества в использовании let с count=0 - с или без let count будет строковой переменной; если вы хотите создать целочисленную переменную, используйте declare -i count=0 (что также прояснит ваше намерение). Обратите внимание, однако, что благодаря утиной печати тип редко имеет значение. При использовании ((...)) вы уже использовали не-POSIX синтаксис bash, так почему бы не использовать [[ ... ]] или ((...)) вместо test? Например, test "$count" -eq 0 && echo "" >> tmpfile && ((count+=1)) можно упростить до (( count++ == 0 )) && echo >> tmpfile.
 – 
mklement0
5 Июл 2014 в 07:42
Кроме того, общее предостережение заключается в том, что зацикливание строк входного файла в чистом коде оболочки нецелесообразно по соображениям производительности - такие утилиты, как sed или awk, намного, намного быстрее.
 – 
mklement0
5 Июл 2014 в 07:45
Оба пункта хорошо приняты. Значение переменной оценивается как арифметическое выражение when it is referenced, или, когда переменной, которой присвоен целочисленный атрибут using declare -i, присваивается значение. (man bash) Согласен, объявлено ли -i или нет, это редко имеет значение. Цикл может иметь свои недостатки для больших файлов, но для всего, что меньше нескольких тысяч строк, цикл будет таким же быстрым или даже быстрее, чем запуск дополнительного приложения для этого.
 – 
David C. Rankin
5 Июл 2014 в 08:10
На самом деле, я только что провел быстрый тест, сравнивая ваше решение с решением на основе sed (sed $'2s/^/\\\n/' file): решение sed начинает работать быстрее при 50. строки (той же длины, что и пример данных в вопросе, в моей системе OS X 10.9.4). Не научный тест, но он дает вам ощущение, что порог, вероятно, ниже, чем вы думаете. Хотя я согласен с тем, что это не имеет значения для небольших файлов, обычно стоит добавить заявление об отказе от ответственности при публикации решения с циклом поверх строк только для оболочки. Что касается вашего конкретного кода: вы должны заключить в двойные кавычки $line в test -n $line.
 – 
mklement0
5 Июл 2014 в 09:16
Вот это хороший тест. У меня не было времени запустить его, но я подозреваю, что результаты будут варьироваться от, скажем, 50, как вы нашли, до тысячи или около того в зависимости от схемы диска, кеша и т. д. sed с одним вызовом из команды line мало что может замедлить его работу, но при повторном вызове, скажем, из скрипта, любое преимущество быстро исчезает. Спасибо, что нашли время, чтобы поставить цифры к нему. Если я найду что-нибудь примечательное, я оставлю заметку здесь.
 – 
David C. Rankin
5 Июл 2014 в 11:20