Использование GNUsed
:
sed -i '$s/,$//' file
То есть в последней строке($
)замените(s
)запятую в конце строки(,$
)ничем.
Изменение будет выполнено в месте -из-за флага -i
.
Со стандартнымsed
:
sed '$s/,$//' <file >file.new &&
mv file.new file
Примечание :Кто-то предложил изменить "в последней строке" на "последний в строке" (или что-то подобное ). Это не правильно. Когда $
используется для указания адреса (строки, где применить команду редактирования ),тогда это относится к последней строке потока или файла. Это отличается от использования $
в регулярном выражении.
Проблема с вашим подходом заключается в том, что повторяющиеся подстановочные знаки интерпретируются не (="расширенными" )"синхронизированным" способом, а заново и независимо для каждого появления в командной строке -. Следовательно, вам нужно будет работать с вложенными циклами оболочки.
Вы можете попробовать следующий сценарий оболочки. Обратите внимание, что он использует bash
функции (ваш вопрос не включал, какую оболочку вы используете)
#!/bin/bash
hdr=0 # initialize variable to keep track of whether the header is already printed
# loop over directories
for d in Chr*
do
# extract trailing number from dir name by removing 'Chr' part (bash feature!)
n="${d#Chr}"
# loop over all files
for f in "$d/"*".$n"
do
if (( hdr == 0 )) # if header wasn't printed yet, output entire file
then
cat "$f" > final_file
hdr=1
else # otherwise, output file content starting with line 2
tail -n +2 "$f" >> final_file
fi
done
done
Вы можете назвать сценарий concatenate.sh
, сделать его исполняемым и запускать его из каталога, в котором расположены все ваши Chr{1..22}
подкаталоги. final_file
также будет создан в этом каталоге.
Заметьте, я не мог проверить это очень далеко, но это не должно ничего разрушать...
Если вы хотите захватить все файлы во всех ваших подкаталогах Chr.*
, вы можете использовать это
cat Chr*/* >final_file
Если вам нужно ограничить набор файлов в каждом подкаталоге, чтобы он соответствовал суффиксу имени этого каталога (, поэтому в Chr1
мы рассматриваем только файлы, соответствующие *.1
), вам понадобится цикл
shopt nullglob # This is bash-specific
for i in {1..22}
do
cat Chr$i/*.$i
done >final_file
Необязательный параметр shopt nullglob
сообщает оболочке, что если подстановочный знак не может совпасть, его следует удалить, а не оставить буквальную звездочку.
В качестве альтернативы, поскольку кажется, что вы хотите, чтобы все строки заголовка, кроме первой, были исключены из ваших конкатенированных файлов, этот расширенный цикл может справиться с этим
first=yes
for i in {1..22}
do
for f in Chr$i/*.$i
do
[[ -n "$first" ]] && head -n1 "$f" && first=
cat "$f"
done
done >final_file
Или, если ваша строка заголовка существует как первая строка первого файла и впоследствии может быть удалена, где бы она ни встречалась, вы можете удалить ее, используя конструкцию, подобную этой
for i in {1..22}
do
cat Chr$i/*.$i
done |
awk '$0 != header { print } header == "" { header = $0 }' >final_file
Вместо этого используйте оболочку zsh
:
cat -- */Chr<1-22>/*.<1-22>(n) > final_file
В zsh
<x-y>
— это оператор подстановки, который соответствует диапазонам десятичных целых чисел, а квалификатор n
glob переключает параметр numericglobsort
, который приводит к числовой сортировке расширений подстановки.
Из другой оболочки вы можете сделать:
zsh -c 'cat -- */Chr<1-22>/*.<1-22>(n) > final_file'
Чтобы пропустить заголовок во всех файлах, кроме первого, и предполагая, что реализация GNU или busyboxtail
(наиболее распространена в системах, использующих Linux в качестве ядра ), вы можете сделать:
(){
cat < $1; shift; (($#)) && tail -qn +2 -- "$@"
} */Chr<1-22>/*.<1-22>(n) > final_file