bash 从文件中执行读取命令

bash 从文件中执行读取命令

经过一些操作后,我得到了包含命令的文件,我很高兴能够执行这些命令。

输入文件(文件名是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 有用。

相关内容