![bash 从文件中执行读取命令](https://linux22.com/image/25120/bash%20%E4%BB%8E%E6%96%87%E4%BB%B6%E4%B8%AD%E6%89%A7%E8%A1%8C%E8%AF%BB%E5%8F%96%E5%91%BD%E4%BB%A4.png)
经过一些操作后,我得到了包含命令的文件,我很高兴能够执行这些命令。
输入文件(文件名是inp2.txt):
"02 - Beautiful Emptiness.mp3"
"02 - Come. mp3"
"02 - Go For It.mp3"
代码:
#!/bin/bash
exec 4<inp2.txt # opening file via descriptor
while read LINE <&4; do
printf "%s\n" "$LINE" # just to watch that command is proper
$LINE # execute command
done
编辑:抱歉,我省略了我认为对理解不重要的代码的重要部分。完整代码:
#!/bin/bash
IFS='
'
declare LINE
declare BUF
declare a
a=1
rm inp2.txt # delete previous version of file
exec 3< inp.txt # open descriptor for a file()content of it is written above
while read LINE <&3; do # read it
printf "mv ""$LINE"" %s\n" """$a"".mp3" >> inp2.txt
# form a command and output it to the file, just to make sure that I got a command that I really want
let "a++" # increment number that will be a part of new unique name
done
exec 4<inp2.txt #open again descriptor with ready commands
while read LINE <&4; do
printf "%s\n" "$LINE" # check again
$LINE # here it should be executed, but I get mistakes that is pointed down
done
exit 0
输出:
看起来命令是正确的,但有些东西是错误的,我无法弄清楚到底哪里出了问题。
答案1
您发布的脚本、数据文件和输出不一致。脚本和数据文件都不包含mv
,但您的屏幕截图包含 。另外,您的屏幕截图提到了第 28 行,而您发布的脚本中没有该行。当您向我们提供不一致的信息时,很难查明您的问题。
也就是说,您正在尝试做两件事之一,但这两件事都无法按照您尝试的方式进行。
如果输入文件包含类似以下的行
mv "02 - Beautiful Emptiness.mp3" 1.mp3
那么它确实是一个shell脚本。不要逐行读取它,而是将其作为 shell 脚本执行。确保您可以信任此文件,因为您将执行其中的任何内容,包括
rm -rf ~
或某些此类文件。. inp2.sh
如果输入文件包含类似以下的行
"02 - Beautiful Emptiness.mp3"
那么你阅读它的方式就不起作用了。
read LINE
执行以下操作:- 读一行;
- 如果该行以反斜杠结尾,则删除反斜杠并读取另一行(重复直到
\
读取到不以 a 结尾的行); - 仅用第二个字符替换所有反斜杠+字符序列;
- 设置
LINE
为读取的行的串联,减去换行符。
当 shell 执行命令时
$LINE
,它会执行当它看到引号外的变量替换时总是执行的操作,即:- 将变量的值在包含空格的每个位置拆分为单词列表(假设默认值为
IFS
); - 将每个单词视为一种全局模式,如果它至少匹配一个文件,则将其扩展。
听起来没什么用?这是。请注意,这里没有任何关于引号的内容:引号是 shell 语法的一部分,而不是 shell 扩展规则的一部分。
您可能应该包含inp2.txt
一个文件名列表,每行一个。看为什么如此频繁地使用“while IFS= read”,而不是“IFS=;”在阅读时..`?了解如何从文件中读取行列表。你会想要类似的东西
i=1
while IFS= read -r source; do
dir=$(dirname -- "$source")
ext=
case "${source##*/}" in
*.*) ext=.${source##*.};;
esac
mv -- "$source" "$dir/$i$ext"
done <inp2.txt
为了完整起见,我会提到另一种可能性,但我不推荐它,因为它很繁琐,而且不会让你做你似乎正在做的事情。像这样的文件
"02 - Beautiful Emptiness.mp3" "02 - Come. mp3" foo\ bar.mp3
然后就可以通过命令来读取了xargs
。输入xargs
是一个以空格分隔的元素列表,它可以是用单引号括起来的文字(可能包含空格)、用双引号括起来的文字(可能包含空格)或可能包含反斜杠转义符的不带引号的文字(\
引用下一个字符)。请注意,该xargs
语法与 shell 可能识别的任何语法都不同。
答案2
从表面上看,您在引用方面遇到了问题。
通过用"$LINE"
引号括起来(在“执行命令”行上),该命令将被视为单个命令,而不是mv
带有一些附加参数的命令。因此,对于初学者来说,您应该删除该行中的引号。
此外,您的路径也会出现问题:我发现您的文件名中包含空格。因此,您必须修改 inp2.txt 文件,以便在任何带有空格的路径/文件名周围添加引号,或者转义空格。
此外,您可能需要尝试将以下内容添加到脚本顶部:
IFS='
'
是的,第二行只是该行的单引号。这将改变脚本中处理空格的方式。
您的脚本将如下所示:
#!/bin/bash
IFS='
'
exec 4<inp2.txt # opening file via descriptor
while read LINE <&4; do
printf "%s\n" "$LINE" # just to watch that command is proper
$LINE # execute command
done
答案3
这个版本的工作原理是构建命令行并将它们输送到 bash 中执行。
#! /bin/bash
i=1
while read LINE ; do
# strip off any leading or trailing double-quotes.
# script now works whether input quotes the filenames or not.
LINE=$(echo "$LINE" | sed -r -e 's/^"|"$//g')
[ -n "$LINE" ] && echo mv \"$LINE\" \"$i.mp3\"
i=$((i + 1))
done < inp2.txt | bash
注意:要对脚本进行试运行测试,请| bash
从最后一行删除 或将其更改为| cat
,这样会产生如下输出:
mv "02 - Beautiful Emptiness.mp3" "1.mp3"
mv "02 - Come. mp3" "2.mp3"
mv "02 - Go For It.mp3" "3.mp3"
该[ -n "$LINE" ]
测试会跳过输入中的任何空行...添加是因为我创建的 inp2.txt 末尾有一个额外的空行。
答案4
你尝试过这样的事情吗?
while read LINE <&4; do
printf "%s\n" "$LINE" # just to watch that command is proper
comm="$LINE" # asssign command to a variable
$comm # execute command
done
它对我使用 bash 有用。