【问题标题】:sed command stuck: sed -n "w $tmpfile" -- st-copyout scriptsed 命令卡住: sed -n "w $tmpfile" -- st-copyout 脚本
【发布时间】:2021-10-11 17:23:49
【问题描述】:

在以下脚本中(表单here

#!/bin/sh
# Using external pipe with st, give a dmenu prompt of recent commands,
# allowing the user to copy the output of one.
# xclip required for this script.
# By Jaywalker and Luke
tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX)
trap 'rm "$tmpfile"' 0 1 15
sed -n "w $tmpfile"
sed -i 's/\x0//g' "$tmpfile"
ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)"
chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard

为什么脚本卡在sed -n "w $tmpfile"

我在sed 手册页中查找了w 命令,上面没有模式也没有输入文件。

它曾经在我的机器上工作过!但不确定发生了什么以及导致它变成这样的原因......

【问题讨论】:

  • 你希望这条线做什么?
  • @Shawn 不确定。可悲的是,我只是脚本的用户。不确定每个特定行的作用。这个脚本的作者在 github 上没有回应这个问题。我的机器好像出了点问题。这里很难,它似乎试图向文件中写入一些东西,但你可以看到那里没有输入文件。
  • 那行 sed -n "w $tmpfile" 将管道输入复制到名称存储在 "$tmpfile" 中的文件中。您可以改用cat > "$tmpfile"。运行tmpfile='foo'; echo 17 | sed -n "w $tmpfile"; cat foo 看看它做了什么。
  • 是的。它在那条线上寻找标准输入。但是这个脚本不需要用户的任何输入。
  • 它需要来自某处的输入,无论是不是用户idk。

标签: linux shell sed


【解决方案1】:

使用sed -n "w $tmpfile"; sed -i 's/\x0//g' "$tmpfile",您将管道输入复制到临时文件,然后使用带有“就地”编辑选项的 sed 来修改该临时文件。与只调用 1 次 sed 相比,这没有任何意义:sed 's/\x0//g' > "$tmpfile"

至于为什么挂起:

  1. 猜测 1:您没有通过管道输入任何输入(第一条评论说 # Using external pipe... 从而告诉您即使您看不懂脚本,该命令也需要管道输入)。
  2. 猜想 2:你现在的机器上的 sed 版本需要备份文件名(例如 BSD sed),所以 sed -i 's/\x0//g' "$tmpfile" 使用 's/\x0//g' 作为备份文件名,"$tmpfile" 作为备份文件名没有输入的脚本。

该命令还有其他几个可移植性、稳健性和效率问题 - 您可能希望将其丢弃并编写不同的脚本。

我又看了一下那个脚本,你绝对应该把它扔掉,因为它有很多错误,例如一行一行地在不知道输入/输出是什么的情况下如此猜测:

#!/bin/sh
  • 应该是#!/usr/bin/env bash
tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX)
  • 应该是tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) || exit
trap 'rm "$tmpfile"' 0 1 15
  • 应该是trap 'rm -f "$tmpfile"; exit' 0 1 15,而 1 和 15 可能不是必需的。
sed -n "w $tmpfile"
sed -i 's/\x0//g' "$tmpfile"
  • 应该只是 1 个命令,sed 's/\x0//g' > "$tmpfile"
ps1="$(grep "\S" "$tmpfile" | tail -n 1 | sed 's/^\s*//' | cut -d' ' -f1)"
  • 应该是ps1=$(awk 'NF{line=$1} END{print line}' "$tmpfile")
chosen="$(grep -F "$ps1" "$tmpfile" | sed '$ d' | tac | dmenu -p "Copy which command's output?" -i -l 10 | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
  • 应该是chosen=$(tac "$tmpfile" | awk -v ps1="$ps1" 'index($0,ps1) && c++' | dmenu -p "Copy which command's output?" -i -l 10)
eps1="$(echo "$ps1" | sed 's/[^^]/[&]/g; s/\^/\\^/g')"
awk "/^$chosen$/{p=1;print;next} p&&/$eps1/{p=0};p" "$tmpfile" | xclip -selection clipboard
  • 应该只是 1 个命令 awk -v chosen="$chosen" -v ps1="$ps1" '$0==chosen{p=1;print;next} p&&index($0,ps1){p=0};p' "$tmpfile" | xclip -selection clipboard

因此,总体而言,如果将脚本编写为:

#!/usr/bin/env bash
tmpfile=$(mktemp /tmp/st-cmd-output.XXXXXX) || exit

trap 'rm -f "$tmpfile"; exit' 0 1 15

sed 's/\x0//g' > "$tmpfile"

ps1=$(awk 'NF{line=$1} END{print line}' "$tmpfile")

chosen=$(tac "$tmpfile" | awk -v ps1="$ps1" 'index($0,ps1) && c++' | dmenu -p "Copy which command's output?" -i -l 10)

awk -v chosen="$chosen" -v ps1="$ps1" '$0==chosen{p=1;print;next} p&&index($0,ps1){p=0};p' "$tmpfile" | xclip -selection clipboard

原始脚本中的那些 sed 's/[^^]/[&]/g; s/\^/\\^/g' 命令试图转义字符串中的所有字符,以使任何正则表达式元字符在 awk 中的正则表达式中使用时都被视为文字,但它们的替换不正确(请参阅 Is it possible to escape regex metacharacters reliably with sed ) 如果您只是在 awk 中使用字符串而不是正则表达式,则无论如何都没有必要。

由于没有任何东西可以测试它,我可能有一些非常轻微的错误,并且可能会有进一步的改进,但我提供的命令将更接近于可移植、健壮、高效的脚本。

【讨论】:

  • 非常感谢您的解释。我会更深入地学习。
  • 对不起。我什么都不知道 :)。我的版本肯定是gnu。 sed (GNU sed) 4.8
  • 我提出的建议适用于任何版本的工具 - GNU、BSD 等等。
  • 因为您正在使用 sh 不支持的结构,例如 $(...) - 如果它在您的系统上运行,那是因为 sh 别名为 bash 或其他一些 shell支持命令替换的语法。我不知道checkbashism 是什么,抱歉。
  • 最终脚本解决了我的问题。我从这篇文章中学到了很多东西。谢谢!!也可以随时编辑/建议我编辑问题正文或标题,以帮助其他人获得此解释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-17
  • 1970-01-01
  • 2013-07-08
  • 2022-07-05
  • 1970-01-01
  • 2017-12-21
  • 2017-03-15
相关资源
最近更新 更多