如何将命令的输出作为参数列表传递给脚本?

如何将命令的输出作为参数列表传递给脚本?

我想将内容添加到当前目录中每个文件的开头。我需要学习如何立即从当前工作目录中获取所有文件名并将它们作为参数;我不需要通过手动指定每个文件作为脚本的参数来完成此操作。我想将这些新知识用于进一步的脚本。我不需要侧面或不同的解决方案。我需要学习如何获取命令 ( ls) 输出并将其作为参数。我尝试但未能做到这一点:

./file-edit.sh $(ls)
./file-edit.sh `ls`

这是我的有效脚本:

#!/bin/bash
lineno=2            # Append from line 2 
BAD_ARGS=85         # Error code

string0="###################################################################"
string2="#Description    :"
string3="#Date           :`date +%d-%B-%Y`"
string4="#Author         :Milos Stojanovic"
string5="#Contact:       :https://www.linkedin.com/in/xxx/"
string6="###################################################################"
# Can be any other strings

if [ ! -f $1 ]; then
        echo "Please specify at least 1 filename as an argument!"
        exit $BAD_ARGS
fi

until [ -z  "$1" ]
do
        string1="#Script Name    :$1"
        file=$1;
        sed -i ""$lineno"i $string0\n$string1\n$string2\n$string3\n$string4\n$string5\n$string6" $file
        echo "File "\'$file\'" altered at line number: $lineno"
        shift 1
done

exit 0

答案1

要在脚本中单独处理每个参数,请在您的脚本中使用循环,如下所示:

for pathname do

    # Insert code here that uses "$pathname".
    # You will not need to shift arguments.

done

要使用当前目录中的所有名称调用脚本,请使用

./script.sh ./*

shell 会将./*模式扩展为包含当前目录中所有名称的列表。

与使用相比,它的优点是ls它不使用ls,因此可以处理包含任何有效字符(甚至空格、制表符和换行符)的文件名。

或者,您可以将文件名通配符移动到脚本本身中(以避免必须在命令行上为脚本提供任何参数)

for pathname in ./*; do
    # Insert code here that uses "$pathname".
done

如果您的脚本需要区分引用常规文件的名称和引用非常规文件的任何名称(例如目录),请-f在循环中使用测试:

# skip non-regular files
[ ! -f "$pathname" ] && continue

这仍然允许通过符号链接到常规文件。要也跳过这些,请将测试更改为

if [ ! -f "$pathname" ] || [ -L "$pathname" ]; then
    # Skip non-regular files and symbolic links.
    continue
fi

其他的建议:

您可以通过在字符串中包含换行符来将多行字符串分配给变量:

boilerplate='Hello and welcome!
This is my program.
Enjoy'

要在文本文档中的第二行之后插入文本,请使用sed-i此处使用进行就地编辑):

printf '%s\n' "$boilerplate" | sed -i '2r /dev/stdin' somefile

或者,使用此处文档,

sed -i '2r /dev/stdin' somefile <<END_BOILERPLATE
Hello and welcome!
This is my program.
Enjoy
END_BOILERPLATE

答案2

由于其他答案没有解决您具体做错了什么,

我尝试过但未能做到这一点:

./file-edit.sh $(ls)

我尝试了你的脚本,我得到:

$ ./file-edit.sh $(ls)
Please specify at least 1 filename as an argument!

在检查 的输出时ls,我注意到它首先列出了目录。如果我移动到没有子目录的目录,我得到:

$ ./file-edit.sh $(ls)
File 'a' altered at line number: 2
File 'b' altered at line number: 2
File 'c' altered at line number: 2 

所以,你检查论点的方式就是问题所在。-f仅在常规文件上成功。它看到为第一个参数提供了一个目录,并给出了一个错误,指出用户没有提供任何文件名参数,即使他们提供了。

如果您想确保至少提供了一个参数,请使用$#it 扩展至提供的参数数量:

if [ "$#" -eq 0 ]; then
  echo "Please specify at least 1 filename as an argument!"
  ...

如果您想确保它们是常规文件,请在循环中进行检查,并检查每个文件,而不仅仅是第一个文件。

until [ -z  "$1" ]
do
        string1="#Script Name    :$1"
        file=$1;
        if [ -f "$file" ]; then
                sed -i "${lineno}i $string0\n$string1\n$string2\n$string3\n$string4\n$string5\n$string6" "$file"
                echo "File '$file' altered at line number: $lineno"
        fi
        shift 1
done

现在,该代码是为了防止您想要忽略非常规文件参数,但是如果您想要的话,您可以轻松地将其更改为在看到非常规文件时退出。

话虽如此,不建议使用ls这样的输出。文件名中可以​​有空格,bash 只会在空格上分割所有内容。如果您有一个文件foo bar.txt,bash 将拆分该文件,您的脚本将查找文件foobar.txt.

从您对问题的评论中,我发现有人一定建议您改用 glob。我不太明白你关于它不起作用的观点。./file-edit.sh *对我来说效果很好。

答案3

我会选择类似的东西:

find <dir> -type f -print0 | xargs -0 sed -i '...'

而不是整个until循环。也应该快一些。

我发布替代方案的原因:

  • 你不应该解析ls输出(600+票)

  • 我宁愿在我的脚本中使用系统机制快速一行,而不是破解我自己的(可能有错误的)多行循环构造。

  • 大多数自建结构都会遇到“带空格的文件”问题,或者遇到参数长度问题。

相关内容