读取循环时的Bash:输出永远不会改变;被缓存?

时间:2015-11-22 21:38:20

标签: linux bash while-loop audio-player

***最终答案:

dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'" |
while read -r line; do
  sleep 1
  echo -e "$(grep '~filename=' $HOME/.quodlibet/current | sed 's/~filename=//')\n$(python2 -c "import sys, urllib as ul; print ul.unquote_plus(sys.argv[1])" "$(quodlibet --print-queue | sed 's|file://||g')")" > $HOME/Dropbox/Playlists/queue
done

竞争条件存在问题,并且使用多个写入来破坏文件,我也用python命令替换了echo -e,因为quodlibet是用python制作的,它可能被解析为解析我不知道首先想要python。这一切现在都有效。我已经完成了大多数音乐播放器已经拥有的quodlibet功能:一个实际存在程序崩溃的队列,并且不需要经常修补和保姆只播放一首歌。 叹息

------------------------原始问题:

我正在使用Arch Linux,我已经创建了一个脚本,每次歌曲结束/开始播放时都应该保存quodlibet的队列。一切都工作除了一件事(所以你不必检查所有这些sed命令的错误或任何东西)。如果我在终端中手动运行所有这些东西,没有任何while循环或DBus - 每次我注意到歌曲结束时只需手动写入文件 - 它工作正常。一旦我将它放入while循环中,每次歌曲开始时它仍然会写入文件,但它每次都会写入完全相同的文件,直到我重新启动脚本。我认为它正在缓存这些命令的输出,而不是更新缓存。

所以,让我们拼出来:quodlibet有它当前正在播放的歌曲以及下一步播放的队列。这是脚本:

while read -r line; do
  grep '~filename=' $HOME/.quodlibet/current \
  | sed 's/~filename=//' > $HOME/Dropbox/Playlists/queue
  echo -e "$(quodlibet --print-queue | sed 's|%|\\\x|g' | sed 's|file://||g')" \
  >> $HOME/Dropbox/Playlists/queue
done <<< "$(dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'")"

每次歌曲改变时,应该发生的是这两个命令将现在播放的歌曲和队列写入文件。相反的是,每当歌曲改变时,这两个命令会写出正在播放的歌曲,并将第一次循环写入文件时排队。

所以,让我们说现在播放的是音轨1,音轨2到10是在队列中。该脚本将其写入文件。

然后,曲目1结束。现在,现在播放是音轨2,队列是音轨3到10。然而,即使循环注意到歌曲改变并写入文件,它所写的内容是音轨1作为正在播放并且跟踪2到10作为队列。再次。并且在此之前,在您知道它之前,它正在播放曲目10并且队列是空的,但文件中仍然包含所有10个曲目。

我尝试在循环外部的终端中手动在循环内运行完全相同的命令,在更改歌曲后,循环脚本正在运行。该文件将反映它应该是什么。但是当歌曲再次改变时,循环会抓住它并再次重写所有10首曲目。换句话说,这些确切的命令只有当它们在循环中时才会做我想要的。这里肯定有一些东西被缓存而且从未更新过。

编辑:看起来我需要澄清一些事情。

1)尽管不应该如此,但我的脚本表现完全相同:

while read -r line; do
  grep '~filename=' $HOME/.quodlibet/current \
  | sed 's/~filename=//' > $HOME/Dropbox/Playlists/queue
  echo -e "$(quodlibet --print-queue | sed 's|%|\\\x|g' | sed 's|file://||g')" \
  >> $HOME/Dropbox/Playlists/queue
done <<< "$(dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'")"

或者:

dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'" |
while read -r line; do
  grep '~filename=' $HOME/.quodlibet/current \
  | sed 's/~filename=//' > $HOME/Dropbox/Playlists/queue
  echo -e "$(quodlibet --print-queue | sed 's|%|\\\x|g' | sed 's|file://||g')" \
  >> $HOME/Dropbox/Playlists/queue
done

2)我在更复杂的东西之前测试了一个更简单的版本。这是我制作的一个脚本:

while true; do
  echo "$(cat /tmp/foo | sed 's/b/n/g')"
done

我尝试在运行循环的中间更改文件/ tmp / foo,就像quodlibet命令的输出自行更改一样,并且它更新得很好。输出改变了它应该的样子。

这不是我开始的,但它是我在继续实际脚本之前做的最后一个。你会看到它包含我正在做的所有事情,除了4件事:quodlibet,dbus命令,&gt;保存或&gt;&gt;追加文件,以及while-read组合。其中一个是无论环境发生什么变化都使输出始终保持不变,我认为我们可以排除quodlibet,因为正如我之前所说,手动运行这些命令可以正常工作。

编辑2:Welp,我没有向下滚动文件,但我刚才做了。这个问题变得更复杂,但可能更容易解决。它并不是每次都写出完全相同的输出。它以某种方式跳过覆盖文件的行 - 以grep开头的行 - 并且JUST附加第二行的输出,echo -e。

编辑3:现在我又难过了。当我将完整的两行复制并粘贴到while循环中并进入bash终端时,它们可以实现我想要的功能。但是在while循环中,第一个grep命令从未实际写入该文件。我想也许是莫名其妙地吃第一个命令,所以我试着事先添加一个空的回声:

dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'" |
while read -r line; do
  echo
  grep '~filename=' $HOME/.quodlibet/current \
  | sed 's/~filename=//' > $HOME/Dropbox/Playlists/queue
  echo -e "$(quodlibet --print-queue | sed 's|%|\\\x|g' | sed 's|file://||g')" \
  >> $HOME/Dropbox/Playlists/queue
done

但它还在吃第一个grep。为什么不保存文件?

编辑4:我已经确认grep命令肯定会输出正确的输出,但不会将其写入文件。

1 个答案:

答案 0 :(得分:0)

为了将你想要做的具体事情与你认为的问题分开,我建议尝试使用一些独立的命令,然后再尝试使用任何花哨的东西。

while read -r line ; do
    echo $line
done <<< $(yes)

请注意,这绝不会输出任何内容。 $(yes)提供的输入永远不会完成,因此外部循环永远不会打印任何内容。至少有一条对你主要职位的评论指出了这一点。

尝试将输出直接连接到循环,而不是使用捕获变量。我不能说这对你有用,但它至少解决了流输出到while循环的一个明显问题。

yes | while read -r line ; do
    echo $line
done

基于此处的其他调试工作,问题似乎是关于歌曲转换的信号与dbus之间的竞争条件以及将新的“当前”歌曲写入磁盘。为了提供更完整的解决方案(当然未经测试),我可以从以下开始:

function urldecode() {
    python -c "import sys, urllib as ul; print ul.unquote_plus(sys.argv[1])" "$@"
}

dbus-monitor --profile "interface='net.sacredchao.QuodLibet',member='SongStarted'" | \
while read -r line; do
    sleep 1 # Adjust as necessary to preventing race conditions
    $CurrentSongFile=$(grep '~filename=' $HOME/.quodlibet/current | sed 's/~filename=//')
    $SongFileQueue=$(urldecode "$(quodlibet --print-queue)") | sed 's_file://__g')
    printf "$CurrentSongFile\n$SongFileQueue" > $HOME/Dropbox/Playlists/queue
done