Bash 个子描述符 shell

Bash descriptors in sub shell

下面的代码片段旨在收听和 select 目录中的几首歌曲:

exec 3<&1

find /some/directory -name '*.mp3' -print0 | xargs -0 bash -c '
    for i; do
        mplayer -ss 10 "$i" 1<&3
        read -p "Select? (y/n)" -n 1 choice 1<&3
        if [ "$choice" = "y" -o "$choice" = "Y" ]; then
            echo "$i" > /tmp/selected_songs.txt
        fi
    done
    '

exec 3<&-

本意是让 mplayer 和 shell read 接受键盘输入,但没有成功!为此,我认为 FD 3 将指向 findxargs 进程的键盘输入。这再次传递给 xargs 执行的 shell,其中执行了 mplayer 和 shell read;但它没有!

这里出了什么问题?

首先,我认为你让这件事变得比它必须的更难了。如果你有 bash 4,请使用 globstar。

shopt -s globstar
for i in /some/directory/**/*.mp3; do
    mplayer -ss 10 "$i"
    read -p "Select? (y/n)" -n 1 choice
    case "$choice" in
        [yY]) echo "$i" >> /tmp/selected_songs.txt;;
    esac
done

即使您不这样做,也可以使用 find … -exec bash -c 'yourscript' _ {} + 而不是 xargs

(我还将您的 > 更改为 >> 因为我假设您不想在每次通过时截断文件。)

至于理解问题,这里有各种复杂的问题,但我会指出一些重要的事情:

  1. 标准输入为 FD 0,但 exec 3<&1 将 1 复制为 3 并打开它以供 阅读
  2. 您似乎在尝试更改键盘输入进行的位置。这很棘手,因为这样做类似于将 EOF 发送到交互式 shell。大多数 shell 遇到这种情况都会关闭。相反,请考虑更改 xargs 获取输入的位置,而不要管键盘。 (BSD xargs 具有相关的 -o 选项。请查看您的联机帮助页。)
  3. 您正在对 mplayer 和无超时读取进行相同的重定向。如果你正在循环阅读,当你提供输入时,你如何知道你在循环中的什么位置?

傻我!应该重定向的是 FD 0——而不是 1。按以下预期工作的版本:

exec 3<&0
find /media/jeenu/USB/ -type f -print0 | xargs -0 bash -c '
    for i; do
        mplayer -ss 10 "$i"
        read -p "Select? (y/n/q)" -n 1 choice
        case "$choice" in
            [yY]) echo "$i" >> /tmp/selected_songs.txt;;
            [qQ]) break;;
        esac
    done 0<&3
    '
exec 3<&-

如果你想要一个带有 bash 的子 shell,那么你可以这样做:

find /media/jeenu/USB/ -type f -print0 | xargs -0 bash <<EOF
#This is a bash subshell
#put your code here
EOF