如何在shell脚本参数中处理" - "?

jm6*_*666 5 bash shell zsh sh

这个问题有三个部分,每个部分都很容易,但结合在一起并不是微不足道的(至少对我而言):)

需要编写一个脚本作为其参数:

  1. 另一个命令的一个名称
  2. 该命令的几个参数
  3. 文件列表

例子:

./my_script head -100 a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' *.txt
Run Code Online (Sandbox Code Playgroud)

等等.

在我的脚本里面由于某种原因我需要区分

  • 什么是命令
  • 该命令的参数是什么
  • 什么是文件

所以写上面例子的最标准方法可能是:

./my_script head -100 -- a.txt b.txt ./xxx/*.txt
./my_script sed -n 's/xxx/aaa/' -- *.txt
Run Code Online (Sandbox Code Playgroud)

问题1:这里有更好的解决方案吗?

在./my_script中处理(第一次尝试):

command="$1";shift
args=`echo $* | sed 's/--.*//'`
filenames=`echo $* | sed 's/.*--//'`

#... some additional processing ...

"$command" "$args" $filenames #execute the command with args and files
Run Code Online (Sandbox Code Playgroud)

filenames包含spaces和/或' - '时,此解决方案将失败,例如
/ some - path/to/more/idiotic file name.txt

问题2:如何正确获取$command$args以及$filenames后续执行?

问题3: - 如何实现以下执行方式?

echo $filenames | $command $args #but want one filename = one line (like ls -1)
Run Code Online (Sandbox Code Playgroud)

这里是不错的shell解决方案,还是需要使用例如perl?

Tom*_*haw 6

首先,听起来你正在尝试编写一个带有命令和文件名列表的脚本,并依次对每个文件名运行命令.这可以在bash中以一行完成:

$ for file in a.txt b.txt ./xxx/*.txt;do head -100 "$file";done
$ for file in *.txt; do sed -n 's/xxx/aaa/' "$file";done

但是,也许我误解了你的意图,所以让我单独回答你的问题.

而不是使用" - "(已经有不同的含义),以下语法对我来说更自然:

./my_script -c "head -100" a.txt b.txt ./xxx/*.txt
./my_script -c "sed -n 's/xxx/aaa/'" *.txt

要在bash中提取参数,请使用getopts:

SCRIPT=$0

while getopts "c:" opt; do
    case $opt in
        c)
            command=$OPTARG
            ;;
    esac
done

shift $((OPTIND-1))

if [ -z "$command" ] || [ -z "$*" ]; then
    echo "Usage: $SCRIPT -c <command> file [file..]"
    exit
fi
Run Code Online (Sandbox Code Playgroud)

如果要为每个剩余的参数运行命令,它将如下所示:

for target in "$@";do
     eval $command \"$target\"
done
Run Code Online (Sandbox Code Playgroud)

如果你想从STDIN读取文件名,它看起来会更像这样:

while read target; do
     eval $command \"$target\"
done
Run Code Online (Sandbox Code Playgroud)

  • 行``filenames = $*`在其他好的代码中是非常可怕的.一般来说,不要使用`$*`.特别是在这种情况下,不要使用`$*`.如果你使用了Bash数组,那么``$ @"`就可以了.实际上,循环最好写成`for $ in"$ @"; 做......; done`.如果文件名实际上不包含空格,那么`$*`就是好的.一旦名称中有空格,`$*'就会错误地处理所有内容. (3认同)