BASH: 在变量中使用进程替换

3
有效的是什么
vimdiff -c 'set diffopt+=iwhite' <(curl -sS '...') <(curl -sS '...')

现在我想要编写一个脚本,允许定义两个或四个URL,并生成上述命令。
目前我拥有的是:
#!/bin/bash

args=(-c 'set diffopt+=iwhite')
while [ -n "$1" ]; do
  args+=(<(curl -sS "$1"))
  shift
done

vimdiff "${args[@]}"

当使用两个URL运行时,结果是vimdiff显示两个没有内容且底部具有相同文件名的标签,例如"/proc/20228/fd/63"。换句话说,进程替代似乎没有被应用和/或错误,因为我至少希望在文件名中看到两个不同的文件描述符。
我猜问题要么在args+=...行,要么在最后一行。

1
你是怎么调用你的脚本的?你提供了什么参数?你也可以使用for i in "$@"; do ... done循环。完全取决于你,但避免了在没有数组的POSIX shell中通常使用的额外shift - undefined
1
你的主要问题是<(curl -sS "$1")args+=参数中被展开,并且不等待vimdiff "${args[@]}"命令。在该命令之前转储args数组以进行检查,例如declare -p args。你应该将命令分开,如vimdiff "${args[@]}" <(curl -sS "$file1") <(curl -sS "$file2"),以确保进程替换不会提前发生。 - undefined
当进程重定向被写在引号内时,它不会被执行。因此,vimdiff将你的<curl ...作为参数接收。 - undefined
5个回答

2
我认为使用命名管道(FIFOs)而不是<(...)来为vimdiff创建临时文件更好,在我的例子中,为每个提供的URL创建了一个命名管道FIFOs,并将curl命令的输出重定向到相应的命名管道,然后将命名管道路径作为参数传递给vimdiff
#!/bin/bash

args=(-c 'set diffopt+=iwhite')
fifo_files=()

cleanup() {
  rm -f "${fifo_files[@]}"
}

trap cleanup EXIT

while [ -n "$1" ]; do
  #creating a named pipe (FIFO) and add it to the array
  fifo=$(mktemp -u)
  mkfifo "$fifo"
  fifo_files+=("$fifo")

  #using curl to write the output to the named pipe
  curl -sS "$1" > "$fifo" &

  shift
done

#pass the named pipes as arguments to vimdiff
vimdiff "${args[@]}" "${fifo_files[@]}"

1
这个方法很有效,而且非常接近你的原始提议。
#!/bin/bash

args=(-c 'set diffopt+=iwhite')
while [ -n "$1" ]; do
  exec {fdnum}< <(curl -sS "$1")
  args+=(/dev/fd/$fdnum)
  shift
done

vimdiff "${args[@]}"

首先,您可以通过进程替换创建所有文件描述符。通过访问它们的内容,您可以轻松地将它们收集到一个数组中,并将该数组传递给vimdiff

1
这段代码使用递归函数构建了一个进程替代列表,而且经过了Shellcheck的检查,非常干净。
#! /bin/bash -p

function vimdiff_urls
{
    local -r nurls=$1

    if (( nurls == 0 )); then
        vimdiff -c 'set diffopt+=iwhite' "${@:2}"
    else
        vimdiff_urls "$((nurls-1))" "${@:3}" <(curl -sS "$2")
    fi
}

vimdiff_urls "$#" "$@"

函数的参数是URL的数量,后跟URL列表,然后是用于curl获取URL的进程替代路径列表。 当还有URL存在时,函数会删除第一个URL,并将URL的进程替代路径添加到进程替代路径列表的末尾。 这样做是因为每个进程替代路径在创建它的函数调用完成之前都保持有效。

甚至都不知道我们可以在shell函数中使用递归。 - undefined

0
这对我来说似乎是一个更好的方法:
declare -p args=()
while [ -n "$1" ]; do
  args+=("<(curl -sS ${1})")
  shift
done

bash -c "diff `declare -p args | grep -o -P '"<(.*?)"' | tr '\n' ' ' | tr -d '"'`"

这个可以用:)
如果你在脚本开头使用set -x运行脚本,你会看到当它执行命令时,两个<(xxx)都在引号内,因此被解释为字符串。这解决了问题。显然,使用bash -c会带来一些安全问题...但是如果脚本只是给你自己使用,这应该不是一个大问题。
然而,使用另一个答案中提到的fifo的脚本似乎更清晰:)

@sjngm 哈哈,至少你不会浪费时间去尝试类似的事情... 哈哈(我还在调查中...某个时候我会移除它) - undefined
谢谢你没有放弃那件事。是的,看起来有点混乱 ;) - undefined

0
我不认为自己是Unix-/BASH专家,所以FIFO管道和管道序列有点超出了解决方案。别误会,学习东西是好的。
无论如何,我只是想让我的相当简单的方法能够工作,基本上只是将一些东西连接起来然后运行,所以这是我的eval(地狱!!!):
args="vimdiff -c 'set diffopt+=iwhite'"
for url in "$@"; do
  args="$args <(curl -sS \"$url\")"
done

eval "$args"

这不是为了生产而设计的,只是用来检查和比较的。

对于那些被“-hell!!!”搞糊涂的人,请参考为什么应该避免在Bash中使用eval,应该使用什么代替? - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接