bash script - расширение переменной процесса подстановки - PullRequest
3 голосов
/ 10 февраля 2012

Я пытаюсь выполнить анализ каждого столбца файла с несколькими столбцами и использую вставку, чтобы объединить столбцы вместе. Я априори не знаю, сколько там столбцов, поэтому я использую «wc -w» и цикл для определения массива команд. Каждая команда является заменой процесса. Следующий скрипт показывает, что я пытаюсь, и вывод отображается после. Примечательно, что если я выведу массив команд на терминал, то с помощью мыши вырезай и вставляй его, он работает нормально, поэтому это должен быть порядок раскрытия переменных и подстановки процесса.

Короче говоря, мне нужно иметь подстановку процесса внутри переменной оболочки. Есть идеи? Заранее спасибо.

-------------- script.sh ----------------

#!/bin/bash
f="file.txt";
echo "File contents"
cat $f; 
         # simple solution
echo; echo "First try";
paste <(cat $f) <(tac $f)
         # now define cmd[1] and cmd[2] and merge together with paste
echo; echo "Second try";
cmd[0]="paste";
cmd[1]="cat $f";
cmd[2]="tac $f";
${cmd[0]} <(${cmd[1]}) <(${cmd[1]})
         # but what I really want is something like:
echo; echo "Third try";
cmd[1]="<(cat $f)";
cmd[2]="<(tac $f)";
${cmd[0]} ${cmd[1]} ${cmd[2]}
         # or even better:
echo; echo "Fourth try";
${cmd[*]}
echo; echo "Show the array";
echo ${cmd[*]}

------------- вывод --------------------

$ ./scipt.sh 
File contents
A B C
D E F
G H I

First try
A B C   G H I
D E F   D E F
G H I   A B C

Second try
A B C   A B C
D E F   D E F
G H I   G H I

Third try
paste: <(cat: No such file or directory

Fourth try
paste: <(cat: No such file or directory

Show the array
paste <(cat file.txt) <(tac file.txt)
$ paste <(cat file.txt) <(tac file.txt)
A B C   G H I
D E F   D E F
G H I   A B C
$ 

В ответ на shellter приведем пример ввода.

    7.74336e-08 7.30689e-08 0.359106        19.981796       -0.160611       0.027
    7.74336e-08 7.30689e-08 0.363938        19.985069       0.041319        0.035
    7.74336e-08 7.30689e-08 0.363133        19.982094       0.041319        0.068
    7.74336e-08 7.30689e-08 0.360716        19.981796       -0.160611       0.006
    7.74336e-08 7.30689e-08 0.361522        19.981796       0.243249        0.049
    7.74336e-08 7.30689e-08 0.357897        19.986260       0.041319        0.035

Таких данных может быть 100 миллионов. Мне нужно отделить каждый столбец, разделить каждый столбец на блоки (скажем) 1000, затем выполнить усреднение каждого элемента в блоках, а затем снова объединить усредненные столбцы. Для приведенного ниже примера, если бы я усреднял только по 2 блокам по 3 элемента в каждом (вместо 100K по 1000 в каждом), то результат из столбца 6 будет:

0.0165     # =(0.027+0.006)/2 - 1st row from each size-3 block
0.042      # =(0.035+0.049)/2 - 2nd row
0.0515     # =(0.068+0.035)/2 - 3rd row

У меня уже есть программа для этого усреднения (это "Some_Complicated_Analysis"), и она отлично работает. Таким образом, все, что мне нужно, чтобы мой сценарий отделил столбцы, передал его в Some_Comp_Analysis, а затем снова объединил различные выходные данные в столбцы с paste. Но файлы v. Большие, и я априори не знаю, сколько там столбцов. Если бы я знал, что будет только 2 столбца, то paste <(${cmd[1]}) <(${cmd[2]}) будет работать нормально.

РЕШЕНИЕ НАЙДЕНО

ОБНОВЛЕНИЕ: ответ найден - как показано в обновлении ответа Гленна Джекмана ниже. Команде paste должен предшествовать eval. Я не знаю точно, почему это необходимо, но без них расширение переменной ${cmd[]} портит процесс подстановки <(...). Ответ выше также помещает двойные кавычки вокруг расширения массива "${cmd[*]}", однако они кажутся не столь важными - хотя без них некоторые другие расширения в cmd[] могут потерпеть неудачу. Тем не менее, eval необходимо.

Ответы [ 3 ]

3 голосов
/ 10 февраля 2012

определяет каждый из "cmd1" и "cmd2" как отдельные массивы

$ cmd1=(cat $f)
$ cmd2=(tac $f)
$ paste <("${cmd1[@]}") <("${cmd2[@]}")
A B C   G H I
D E F   D E F
G H I   A B C

обновление: вам просто нужно eval ваши сконструированные замены процесса:

cols=$(head -n 1 $f|wc -w)
for (( i=1 ; i<=cols ; i++ )); do
  cmd[i]="<(cat $f|cut -f$i|Some_Complicated_Analysis)"
done
eval paste "${cmd[*]}"   # quotes are important here
1 голос
/ 08 марта 2012

Это может быть полезно, если ваш скрипт все еще не работает.Все еще следуйте ответу Гленна Джекмана, но, кроме того, вы можете сделать это внутри скрипта

set + o posix

http://www.linuxjournal.com/content/shell-process-redirection

По ссылке: "Процессподстановка не является POSIX-совместимой функцией, и, возможно, ее необходимо включить с помощью: set + o posix "

0 голосов
/ 11 февраля 2012

Попробуйте это:

{
  cat -<<EOS
10 7.74336e-08 7.30689e-08 0.359106        19.981796       -0.160611       0.027
10 7.74336e-08 7.30689e-08 0.363938        19.985069       0.041319        0.035
10 7.74336e-08 7.30689e-08 0.363133        19.982094       0.041319        0.068
10 7.74336e-08 7.30689e-08 0.360716        19.981796       -0.160611       0.006
10 7.74336e-08 7.30689e-08 0.361522        19.981796       0.243249        0.049
10 7.74336e-08 7.30689e-08 0.357897        19.986260       0.041319        0.035
EOS
} |
awk '
  BEGIN { binSz=3; binSzLim=binSz ; binSzLim++ }
  NR==1{
    # base error checking on number of cols in first record
    maxCols=NF
    maxColsLim=maxCols ; maxColsLim++
    r=0
  }
  {
    if (NF != maxCols) {
      print "Skipping record, Mismatch in data, expected " maxCols ", found " NF " recs at " NR ":" $0
      next
    }
    r++
    #dbg print "r="r" NR=" NR ":$0=" $0

    # load data into temp arr[] by column
    for (c=1;c<maxColsLim;c++) {
      arr[r,c]+=$c
      #dbg printf ("arr["r","c"]=" arr[r,c] " " )

      avgArr[r,c]++
      #dbg print "avgArr["r","c"]="avgArr[r,c]
    }

    if(r>=binSz) {
      r=0
    }
  }
  END {
    for (r=1;r<binSzLim;r++) {
      #dbg print "r=" r " binSzLim=" binSzLim " " (r<binSzLim) "\t"
      for (c=1;c<maxColsLim;c++) {
        #dbg printf("arr["r","c"]=" arr[r,c] "\tavg=" arr[r,c]/binSz "\t")
        printf(  arr[r,c]/avgArr[r,c] " ")
      }
      printf "\n"
    }
  }
'

Производит продукцию

10 7.74336e-08 7.30689e-08 0.359911 19.9818 -0.160611 0.0165
10 7.74336e-08 7.30689e-08 0.36273 19.9834 0.142284 0.042
10 7.74336e-08 7.30689e-08 0.360515 19.9842 0.041319 0.0515

Я добавил в данные первый столбец с 10, чтобы упростить отладку этой суммы, и avg работали правильно.

Интересная проблема, спасибо за публикацию и спасибо за вашу добрую волю, продолжая отвечать на мои вопросы ;-)

{ cat -<<EOS ... EOS }| был просто для того, чтобы было легко запустить все это в одном копировании / вставке и увидеть, что оно работает. Вы можете поместить код awk, если файл с #!/bin/awk -f вверху, chmod 755 myScript.awk и запустить его как myScript.awk BigFile > AvgsFile.

Вам нужно только изменить значение на binSz=1000 в блоке BEGIN, чтобы обработать ваши файлы, как вы и планировали.

Мне было бы интересно узнать, каково время для этой версии.

...