将while循环的输出发送到bash函数

时间:2018-09-20 20:52:36

标签: bash shell scope pipe parameter-passing

我创建了一个.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 :(得分:1)

$@$*$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之所以只能使用一次,是因为您消费一个流。一旦消耗掉,它就消失了。 "you can't eat your cake and have it too."


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

在交互式bash提示符下键入help readarray了解更多信息。

答案 1 :(得分:1)

想象一个脚本:

 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. 在命令替换中调用命令会传递标准输入(即in=$(cat)会像普通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.txtsource file.txt。这将“加载”文件,即。使其成为脚本的一部分,语法就是bash。因此您可以:

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

这是在/ etc /中创建配置文件的通用方法,然后由脚本加载它们。这就是为什么在许多/ etc /文件中注释都以#开头的原因。

注意:

  1. 始终将变量括起来。 echo "$1" printArguments "$1" echo "$@" echo "$#" cat "$2" "$3" "$4" [ "$line" == ... ],不错的读物是here
  2. 使用tr -d '\n'删除换行符
  3. ( )创建一个子shell,该子shell创建一个具有新变量且不与父代共享变量的新shell,请参见here