为什么我的shell命令在提示符下工作,而不是作为bash脚本?

时间:2016-04-15 02:32:04

标签: linux bash shell

bash脚本新手。我很熟悉shell脚本。我为客户端编写了这个文本转换脚本。并提取我想要的网址和文章标题。真棒。

echo $(var=$(curl -L website.com/news)) | 
  grep -Po '<h3 class="article-link"><a href="\K[^<]+' <<< $var |
    result=$(sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g') ; let this=0 ; echo "$result" | while read line ; do if ((this % 2 == 0 )) ; then echo website.com/news$line ; else echo $line ; fi ; let this+=1 ; done

当我尝试将其提取到文件并使用bash或sh myThing.sh运行时,它根本不起作用。唯一回应的是&#39; webiste.com/news' ;,当我试图回应$ this时,我得到的只是1.我做错了什么?

#!/bin/bash   
echo $(var=$(curl -L website.com/news)) | 
  grep -Po '<h3 class="article-link"><a href="\K[^<]+' <<< $var |
    result=$(sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g') 

 let this=0 

 echo "$result" | while read line 
 do 
    if ((this % 2 == 0 )) 
    then 
        echo website.com/news$line 
    else 
        echo $line 
    fi 
    let this+=1 
done

编辑:

#!/bin/bash

var=$(curl -L linux.com/news) 
select=$(grep -Po '<h3 class="article-list__title"><a href="\K[^<]+' <<< $var)
result=$(sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g') 

 let this=0 

 echo "$result" | while read line 
 do 
    if ((this % 2 == 0 )) 
    then 
        echo website.com/news$line 
    else 
        echo $line 
    fi 
    let this+=1 
done

2 个答案:

答案 0 :(得分:3)

这个答案解决了OP的特定问题,但解决了问题&#34;为什么我的shell命令在提示符下工作,但不是一个bash脚本?&#34; 一般 Etan Reisner在评论中提供了一个很好的答案:
&#34;你要么没有运行那个确切的命令,要么&#34;工作&#34;因为你的贝壳状态会以你所采取的方式影响事物的工作&#34;并且你的脚本没有那种状态。尝试启动一个全新的shell会话,看看该命令本身是否适用于你。&#34;

echo $(var=...)会为变量$var分配值,但不会输出任何内容,因此echo命令只会打印换行符。

此外,因为$var的赋值发生在$(...)内(命令替换),所以它被限制在 subshel​​l 中,替换中的命令会进入,所以$var在调用shell中定义 (子shell是进程,其中包含当前shell环境的重复,但无法修改当前shell& #39;环境)。

更一般地说,你无法在管道中有意义地定义变量 - 它们既不会对其他管道段可见,也不会在管道完成后显示。 [1 ]

您的[原始]命令可能有效的唯一原因是$var在您的shell中有预先存在的值 。 事实上,假设您通过here-string(grep)向<<<提供输入,管道的第一段(echo ...)完全被忽略< / em>的

要将curl的输出通过管道传递到grep然后传递到sed根本不需要任何中间变量。
此外,您的sed命令缺少输入:您可能打算在第一次尝试时提供$var,在第二次尝试时提供$select(您的第二次尝试来了)接近正确的解决方案)。

你最终可能会寻找什么:

result=$(curl -L website.com/news | 
  grep -Po '<h3 class="article-link"><a href="\K[^<]+' |
    sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g')

# ... processing of "$result"

一些补充说明:

  • 您可以将3个sed来电合并为一个。
  • 您可以将管道输出直接输入while循环,而无需中间变量$result
  • 您通常应该双引号变量引用(例如,使用"$line"而不是$line来保护它们免受shell的解释(分词,通配)。
  • let this+=1在现代Bash中更好地表达为(( ++this ))
  • 我的
  • This answer包含用于了解bash
  • 的资源链接

[1]默认情况下,管道中涉及的所有命令都在bash子shell 中运行,因此它们都会看到副本 shell变量的。 Bash 4.2+提供lastpipe选项(默认情况下关闭),允许您通过运行<在当前 shell而不是子shell 中创建变量当前shell而不是子shell中的em> last 管道段(仅限),以便在管道完成后继续存在... | while read -r line ...$line等场景。
请注意,这仍然无法在较早的管道段中定义变量,希望以后的段会看到它 - 这可能从不工作,因为构成管道的命令是同时启动 ,只有通过协调输入和输出流才能实现有效的从左到右处理。

答案 1 :(得分:1)

这条线完全错了。你试图通过管道传递每个进程的标准输出,当它们都没有打印出除标准错误之外的任何东西。

echo $(var=$(curl -L website.com/news)) | grep -Po '<h3 class="article-link"><a href="\K[^<]+' <<< $var | result=$(sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g') 

我会打破我认为你试图做的事情。

echo $(var=$(curl -: website.com/news))

上面的代码只会打印标准错误,这是一个独立的流而不是标准输出。标准输出分配给$var。但是,您试图将标准输出传递给下一个进程,此时此过程只是一个换行符。

grep -Po '<h3 class="article-link"><a href="\K[^<]+' <<< $var

here-string <<<优先于管道。但变量$var会丢失,因为它是在子shell中定义的,而不是在父shell中定义的。感谢@mklement0

完成所有这些的正确方法是不使用$var。您想要的只是$result中存储的值。

result=$(curl -L website.com/news | grep -Po '<h3 class="article-link"><a href="\K[^<]+'| sed 's/"/\n/g' | sed 's/ \//\n\//g' | sed 's/>//g')

我不打算优化您的脚本。这是一个建议的解决方案。对您的问题的更全面的回答为什么我的shell命令在提示符下工作,而不是作为bash脚本?mklement0 here回答。