Bash:从标准输入或命令行参数循环遍历行的简洁方法?

Bash:从标准输入或命令行参数循环遍历行的简洁方法?

我有一个 bash 脚本,我想循环遍历 stdin 中的行,或者循环遍历传入的每个参数。

有没有一种干净的方法来编写这个,这样我就不必有两个循环?

#!/bin/bash

# if we have command line args... 
if [ -t 0 ]
then
  # loop over arguments
  for arg in "$@" 
  do
    # process each argument
  done
else
  # loop over lines from stdin
  while IFS= read -r line; do
    # process each line
  done
fi

编辑:我正在寻找一个仅使用单个循环的通用解决方案,因为我发现我想经常这样做,但总是写出 2 个循环,然后调用一个函数。那么也许可以将 stdin 转换为数组,这样我就可以使用单个循环来代替?

答案1

为循环创建数据while read

#!/bin/sh

if [ "$#" -gt 0 ]; then
    # We have command line arguments.
    # Output them with newlines in-between.
    printf '%s\n' "$@"
else
    # No command line arguments.
    # Just pass stdin on.
    cat
fi |
while IFS= read -r string; do
    printf 'Got "%s"\n' "$string"
done

请注意,您的示例可以通过替换为或类似的循环concat来完成。while readtr '\n' ','

此外,该-t测试没有说明您是否有命令行参数。


或者,处理两个都命令行参数和标准输入(按顺序):

#!/bin/sh

{
    if [ "$#" -gt 0 ]; then
        # We have command line arguments.
        # Output them with newlines in-between.
        printf '%s\n' "$@"
    fi

    if [ ! -t 0 ]; then
        # Pass stdin on.
        cat
    fi
} |
while IFS= read -r string; do
    printf 'Got "%s"\n' "$string"
done

或者,使用一些人似乎喜欢的快捷符号:

#!/bin/sh

{
    [ "$#" -gt 0 ] && printf '%s\n' "$@"
    [ ! -t 0 ]     && cat
} |
while IFS= read -r string; do
    printf 'Got "%s"\n' "$string"
done

答案2

我们还可以使用标准输入重定向:

#!/usr/bin/env bash
test -t 0 && exec < <(printf '%s\n' "$@")
while IFS= read -r line; do
    echo "$line"
done

测试:

test.sh Hello World
test.sh < /etc/passwd

答案3

具体来说bash,你可以这样做:

if [ -t 0 ]; then
  args=("$@")
else
  readarray -t args
fi
for i in "${args[@]}"; do
   ...
done

答案4

您还可以访问 STDIN 描述符,因此:

for i in $(cat /dev/stdin) ; do echo $i ; done

相关内容