Отправка вывода цикла while в функцию bash - PullRequest
0 голосов
/ 20 сентября 2018

Я создал файл .bashrc, в котором у меня есть две функции.Один проходит по строкам файла в цикле while.Я пытаюсь сохранить содержимое строк, если они соответствуют определенным условиям, а затем передать все три совпадения второй функции, которая затем отобразит их.Тем не менее, я попытался экспортировать переменные, и я также попытался передать во вторую функцию, но ни одна из них не работает.Трубопровод также действует очень странно, как я попытаюсь проиллюстрировать в своем примере кода.

readAndPipe() {
  while read -r line || [[ -n "$line" ]]; do
  (   
  if [[ $line == FIRSTNAME=BOB ]]; then
     echo $line;
  fi; 
  if [[ $line == LASTNAME=SMITH ]]; then
     echo $line;
  fi; 
  if [[ $line == BIRTHMONTH=AUGUST ]]; then
     echo $line;
  fi; 
  ); done < "file.txt" | printArguments $1 #pass the original command line argument;
}

printArguments() {
    #This is where the weirdness happens
    echo $@                   #Prints: only the original command line argument
    echo $#                   #Prints: 1
    echo $2 $3 $4             #Prints nothing
    varName=$(cat $2 $3 $4)
    echo $varName             #Prints: FIRSTNAME=BOB
                              #        LASTNAME=SMITH
                              #        BIRTHMONTH=AUGUST
    cat $2 $3 $4              #Prints nothing
    echo $(cat $2 $3 $4)      #Prints nothing
    cat $2 $3 $4 | tr "\n" '' #Prints tr: empty string2
}

Очевидно, я не эксперт по bash, поэтому я уверен, что здесь много ошибок, но яМне интересно,

  1. Что это за кажущиеся магическими аргументы $ 2 $ 3 $ 4, которые не выводятся с помощью echo, но могут быть использованы cat ровно один раз.
  2. Как правильно сохранить содержимое во время цикла while и передать его другой функции, чтобы я мог отобразить его?

Ответы [ 2 ]

0 голосов
/ 21 сентября 2018

Представьте себе скрипт:

 func() {
       echo $#  # will print 2, func were executed with 2 arguments
       echo "$@"  # will print `arg1 arg2`, ie. the function arguments
       in=$(cat)   # will pass stdin to `cat` function and save cat's stdout into a variable
       echo "$in" # will print `1 2 3`
 }
 echo 1 2 3 | func arg1 arg2
 #                  ^^^^^^ function `func` arguments
 #          ^ passed one command stdout to other command stdin
 # ^^^ outputs `1 2 3` on process stdout
  1. cat, вызываемый без каких-либо аргументов, читает stdin и выводит его на stdout.
  2. Вызов команды в командеподстановка передает stdin вдоль (т. е. in=$(cat) будет читать stdin как обычно cat просто сохраняет вывод (т. е. стандартный вывод cat) в переменную)

К вашему сценарию:

readAndPipe() {
  # the while read line does not matter, but it outputs something on stdout
  while read -r line || [[ -n "$line" ]]; do
         echo print something 
  # the content of `file.txt` is passed as while read input
  done < "file.txt" | printArguments $1 # the `print something` (the while loop stdout output) is passed as stdin to the printArguments function
}

printArguments() {
    # here $# is equal to 1
    # $1 is equal to passed $1 (unless expanded, will get to that)
    # $2 $3 $4 expand to nothing
    varName=$(cat $2 $3 $4) # this executes varName=$(cat) as $2 $3 $4 expand to nothing
    # now after this point stdin has been read (it can be read once, it's a stream or pipe
    # is you execute `cat` again it will block (waiting for more input) or fail (will receive EOF - end of file)
    echo $varName             #Prints: `print something` as it was passed on stdin to this function
}

Если файл file.txt содержит только:

FIRSTNAME=BOB
LASTNAME=SMITH
BIRTHMONTH=AUGUST

, вы можете просто загрузить файл . file.txt или source file.txt.Это «загрузит» файл, т.е.сделайте это частью вашего скрипта, синтаксис bash.Таким образом, вы можете:

. file.txt
echo "$FIRSTNAME"
echo "$LASTNAME" 
echo "$BIRTHMONTH"

Это обычный способ создания файлов конфигурации в / etc /, а затем они загружаются скриптами.Вот почему во многих файлах / etc / files комментарии начинаются с #.

Примечания:

  1. Всегда заключайте ваши переменные.echo "$1" printArguments "$1" echo "$@" echo "$#" cat "$2" "$3" "$4" [ "$line" == ... ], хорошее чтение здесь
  2. Удалите новые строки с помощью tr -d '\n'
  3. A ( ) создает подоболочку, которая создает новую оболочку, которая имеет новые переменные и не разделяет переменную с родителем, см. здесь .
0 голосов
/ 21 сентября 2018

$@, $*, $1, $2 и т. Д. - это аргументы , которые передаются в функцию.Например, в myfunc foo bar baz у нас есть $ 1 == foo, $ 2 == bar и $ 3 == baz.

Когда вы передаете данные в свою функцию, вы должны извлечь их из stdin:

myfunc() {
    data=$(cat)
    echo "I received: >$data<"
}
for n in {1..5}; do echo "x=$n"; done | myfunc

производит

I received: >x=1
x=2
x=3
x=4
x=5<

varName=$(cat $2 $3 $4) работает, потому что $ 2 $ 3 и $ 4 пусты , поэтому оболочка видит это:
varName=$(cat )

Причина, по которой cat "работает только один раз", заключается в том, что вы потребляете поток.Как только вы его потребляете, его уже нет. «Вы не можете съесть свой торт и иметь его тоже».


Функция printArguments может использовать команду readarray для захвата входящих строк в массив,вместо использования cat для захвата всего входящего текста в переменную:

printArguments() {
    readarray -t lines
    echo "I have ${#lines[@]} lines"
    echo "they are:"
    printf ">>%s\n" "${lines[@]}"
}
{ echo foo; echo bar; echo baz; } | printArguments

выводит

I have 3 lines
they are:
>>foo
>>bar
>>baz

Чтобы узнать больше, введите help readarray в интерактивной подсказке bash.

...