#bash #shell #for-loop #awk
#bash #оболочка #для цикла #awk
Вопрос:
я новичок в программировании.У меня много файлов в каталоге, как показано ниже: и каждый файл состоит из двух столбцов данных.
TS.TST_X.1990-11-22
TS.TST_Y.1990-11-22
TS.TST_Z.1990-11-22
TS.TST_X.1990-12-30
TS.TST_Y.1990-12-30
TS.TST_Z.1990-12-30
Сначала я хочу выбрать только вторые столбцы всех файлов с одинаковыми именами (разница только в строках X, Y, Z) (TS.TST_X.1990-11-22, TS.TST_Y.1990-11-22, TS.TST_Z.1990-11-22) и хочу сохранить выходные данные в файле типа TSTST19901112
Аналогично для (TS.TST_X.1990-12-30, TS.TST_Y.1990-12-30, TS.TST_Z.1990-12-30 ) файлов также и хотите сохранить выходные данные, такие как TSTST19901230
Например: если файлы содержат, как показано ниже
TS.TST_X.1990-11-22 TS.TST_Y.1990-11-22 TS.TST_Z.1990-11-22
1 2 1 3.4 1 2.1
2 5 2 2.4 2 4.2
3 2 3 1.2 3 1.0
4 4 4 2.4 4 3.5
5 8 5 6.3 5 1.8
Тогда выходной файл TSTST19901122 будет иметь вид
2 3.4 2.1
5 2.4 4.2
2 1.2 1.0
4 2.4 3.5
8 6.3 1.8
я попробовал код
#!/bin/sh
for file in /home/min/data/*
do
awk '{print $2}' $file
done
Но мой написанный код считывает только столбец всех файлов и не дает ожидаемого результата.Итак, здесь мне нужна помощь экспертов.
Комментарии:
1. Используете ли вы
sh
илиbash
?sh
( Bourne-shell ) обычно нетbash
( Bourne-again shell ).2. Добро пожаловать в SO, спасибо, что показали свои попытки в виде кода. Не могли бы вы, пожалуйста, сообщить нам, хотите ли вы сравнить 1-й столбец всех файлов и сравнить их по значениям? Или мы могли бы просто объединить файлы, не проверяя 1-е поле? Пожалуйста, подтвердите один раз
3. @RavinderSingh13 мне вообще не нужен first coulmn..
4. @glkshu, я не упоминал о необходимости их использования в выходных данных. Я имел в виду, хотите ли вы сравнить значения 1-го столбца из файла в файл? Например, строка 1 file1 имеет 1 в столбце 1dt, а строка 2 file2 имеет 2 в 1-м столбце, в случае rat они должны быть объединены?
5. нет, сэр, мне не нужно
Ответ №1:
Надеюсь, приведенный ниже пример поможет вам начать, в следующий раз, когда вы будете публиковать, поэтому убедитесь, что вы правильно разместили ввод, чтобы читателям было легко помочь вам:
Вот онлайн : DEMO
[akshay@db1 tmp]$ cat test.sh
#!/usr/bin/env bash
# use sort and uniq where field sep being dot,
# we get unique date
while IFS= read -r f; do
# creates veriable like TS.TST_*.1990-11-22
i=$(sed 's/_[^.]/_*/' <<<"$f");
# modify outfile if you want any extension suffix etc
outfile=$(sed 's/[^[:alnum:]]//g' <<<"$i")".txt";
# filename expansion with unquoted variable
# finally use awk to print whatever you want
paste $i | awk 'NR>1{for(i=2; i<=NF; i =2)printf "%s%s", $(i), (i<NF ? OFS : ORS)}' >"$outfile"
done < <(printf '%sn' TS.TST* | sort -t'.' -u -nk3)
[akshay@db1 tmp]$ bash test.sh
[akshay@db1 tmp]$ cat TSTST19901122.txt
2 3.4 2.1
5 2.4 4.2
2 1.2 1.0
4 2.4 3.5
8 6.3 1.8
Ввод:
[akshay@db1 tmp]$ ls TS.TST* -1
TS.TST_X.1990-11-22
TS.TST_Y.1990-11-22
TS.TST_Z.1990-11-22
[akshay@db1 tmp]$ for i in TS.TST*; do cat "$i"; done
TS.TST_X.1990-11-22
1 2
2 5
3 2
4 4
5 8
TS.TST_Y.1990-11-22
1 3.4
2 2.4
3 1.2
4 2.4
5 6.3
TS.TST_Z.1990-11-22
1 2.1
2 4.2
3 1.0
4 3.5
5 1.8
Комментарии:
1. Скопируйте / вставьте это в shellcheck, net , и он расскажет вам о некоторых проблемах. Возможно, он вам не расскажет о том, что
for f in $(ls TS.TST* | ...); do ...; done
должно бытьwhile IFS= read -r f; do ... done < <(printf '%sn' TS.TST* | ...)
или похожим (или лучше :-)!).2. Спасибо, я исправлю, как только вернусь к своей системе. Теперь на мобильном
3. @EdMorton изменен, как вы предложили, однако у меня все еще есть переменная без кавычек
Ответ №2:
РЕДАКТИРОВАТЬ: поскольку OP упомянул в комментариях, что фактические имена файлов немного отличаются, поэтому добавьте решение в соответствии с этим здесь (поскольку в соответствии с OP существует только 3 типа файлов с разным годом и месяцем)..
for file in TS.TST_BHE*
do
year=${file/*./}
year=${year//-/}
yfile=${file/BHE/BHN}
zfile=${file/BHE/BHZ}
outfile="TSTST.$year"
##echo $file $yfile $zfile
paste "$file" "$yfile" "$zfile" | awk '{print $2,$4,$6}' > "$outfile"
done
Объяснение: добавление подробного объяснения выше.
for file in TS.TST_BHE*
##Going through TS.TST_BHE named files in for loop here, where variable file will have its name in it.
do
year=${file/*./}
##Creating year where removing everything till . here.
year=${year//-/}
##Substituting all - with null in year variable.
yfile=${file/BHE/BHN}
##Substituting BHE with BHN in file variable and saving it to yfile here.
zfile=${file/BHE/BHZ}
##Substituting BHE with BHZ in file variable and saving it to zfile here.
outfile="TSTST.$year"
##Creating outfile which has TSTST. with year variable value here.
##echo $file $yfile $zfile
paste "$file" "$yfile" "$zfile" | awk '{print $2,$4,$6}' > "$outfile"
##using paste to contenate values of 3 of the files(BHE BHN and BHZ) and printing only 2nd, 4th and 6th fields out of it.
done
Не могли бы вы, пожалуйста, попробовать следующее, основываясь на комментарии OP, что мы могли бы просто объединить Input_files без проверки значения 1-го столбца.
for file in TS.TST_X*
do
year=${file/*./}
year=${year//-/}
yfile=${file/X/Y}
zfile=${file/X/Z}
outfile="TSTST.$year"
###echo $file $yfile $zfile ##Just to print variable values(optional)
paste "$file" "$yfile" "$zfile" | awk '{print $2,$4,$6}' > "$outfile"
done
Для отображения образцов вывод будет следующим, выше будет сгенерировано имя файла d TS.TST_X.19901122
для показанных образцов.
cat TSTST.19901122
2 3.4 2.1
5 2.4 4.2
2 1.2 1.0
4 2.4 3.5
8 6.3 1.8
Ответ №3:
Следующее воссоздание входных файлов:
cat <<EOF >TS.TST_X.2000-11-22
1 2
2 5
3 2
4 4
5 8
EOF
cat <<EOF >TS.TST_Y.2000-11-22
1 3.4
2 2.4
3 1.2
4 2.4
5 6.3
EOF
cat <<EOF >TS.TST_Z.2000-11-22
1 2.1
2 4.2
3 1.0
4 3.5
5 1.8
EOF
cat <<EOF >TS.TST_X.1990-11-22
1 2
2 5
3 2
4 4
5 8
EOF
cat <<EOF >TS.TST_Y.1990-11-22
1 3.4
2 2.4
3 1.2
4 2.4
5 6.3
EOF
cat <<EOF >TS.TST_Z.1990-11-22
1 2.1
2 4.2
3 1.0
4 3.5
5 1.8
EOF
При запуске со следующим скриптом на repl:
# get the filenames
find . -maxdepth 1 -name "TS.TST*" -printf "%fn" |
# meh, sort them, so it looks nice
sort |
# group files according to suffix after the dot
awk -F. '
{ a[$3]=a[$3]" "$0 }
END{ for (i in a) print i, a[i] }
' |
# here we have: YYYY-MM-DD filename1 filename2 filename3
# let's transform it into TSTSTYYYYMMDD filename{1,2,3}
sed -E 's/^([0-9]{4})-([0-9]{2})-([0-9]{2})/TSTST123/' |
while IFS=' ' read -r new f1 f2 f3; do
# get second column from all files
# if your awk doesn't sort files, they would have to be sorted here
paste "$f1" "$f2" "$f3" | awk '{print $2,$4,$6}' > "$new"
done
# just output
for i in TSTST*; do echo "$i"; cat "$i"; done
Генерирует следующий вывод:
TSTST19901122
2 3.4 2.1
5 2.4 4.2
2 1.2 1.0
4 2.4 3.5
8 6.3 1.8
TSTST20001122
2 3.4 2.1
5 2.4 4.2
2 1.2 1.0
4 2.4 3.5
8 6.3 1.8
Я бы посоветовал провести исследование основных команд оболочки. Прочитайте документацию о find
. Прочитайте введение в awk
и sed
сценарии. Прочитайте хорошее введение в bash, узнайте, как перебирать, сортировать, объединять и фильтровать список файлов в bash. А также прочитайте, как читать поток построчно.