如何使用`sed`来改变bash脚本中的变量?

时间:2016-11-19 22:08:01

标签: bash sed

我正在尝试使用enscript从Mutt打印PDF,并遇到字符编码问题。围绕它们的一种方法似乎是使用sed来替换问题字符:sed -ir 's/[“”]/"/g' {input}

我的测试输入文件是:

“very dirty”    
we’re 

我希望得到"very dirty"we're,但我仍然得到

â\200\234very dirtyâ\200\235
weâ\200\231re

我在printing to PDFs from Mutt找到了一个很好的小帖子,我用它作为起点。我有一个bash脚本,我从.muttrc set print_command="$HOME/.mutt/print.sh"指向该脚本 - 脚本目前的内容如下:

#!/bin/bash
input="$1" pdir="$HOME/Desktop" open_pdf=evince


# Straighten out curly quotes

sed -ir 's/[“”]/"/g' $input
sed -ir "s/[’]/'/g" $input


tmpfile="`mktemp $pdir/mutt_XXXXXXXX.pdf`"
enscript --font=Courier8 $input -2r --word-wrap --fancy-header=mutt -p - 2>/dev/null | ps2pdf - $tmpfile
$open_pdf $tmpfile >/dev/null 2>&1 &
sleep 1
rm $tmpfile

它在创建PDF方面做得很好(如果你把它作为参数给它一个文件也可以正常工作),但我无法弄清楚如何修复卷曲引号。

我在sed行尝试了很多变种:

input=sed -r 's/[“”]/"/g' $input

$input=sed -ir "s/[’]/'/g" $input

根据Can I use sed to manipulate a variable in bash?的建议我也尝试了input=$(sed -r 's/[“”]/"/g' <<< $input)并收到错误:“语法错误:重定向意外”

但没有人真正改变$input - 使用$input更改sed的正确语法是什么?

注意:我接受了一个解决了我问的问题的答案,但正如您从评论中看到的那样,这里还有其他一些问题。 enscript将整个文件作为变量,而不仅仅是文件的文本。因此,尝试调整文件中的文本将需要一些额外的步骤。我还在学习。

2 个答案:

答案 0 :(得分:5)

一般编辑变量

BashFAQ #21是关于在bash中执行搜索和替换操作的综合参考,包括在变量中,因此建议阅读。在这个特例上:

使用shell的本机字符串操作;这比离开子shell,在其中启动外部进程以及读取外部进程的输出要高得多。 BashFAQ #100详细介绍了此主题,非常值得一读。

根据您的bash版本和配置的区域设置,可能会使用括号表达式(即。[“”],就像您的原始代码一样)。但是,最便携的是分别处理,即使没有多字节字符支持也可以使用。

input='“hello ’cruel’ world”'
input=${input//'“'/'"'}
input=${input//'”'/'"'}
input=${input//'’'/"'"}
printf '%s\n' "$input"

...正确输出:

"hello 'cruel' world"

使用sed

要提供字面答案 - 您的几乎在您的问题中采用了基于sed的有效方法。

input=$(sed -r 's/[“”]/"/g' <<<"$input")

...在$input的参数扩展周围添加了缺少的语法双引号,确保它被视为单个标记,无论它是如何进行字符串拆分或全局展开的。< / p>

但可能无法帮助......

提到以下内容是因为您的测试脚本正在操作在命令行上传递的内容;如果生产中不是这种情况,你可以忽略以下内容。

如果您的脚本被调用为./yourscript “hello * ’cruel’ * world”,那么在脚本启动之前,有关用户确切输入的内容的信息将丢失,而您无法在此处执行任何操作

这是因为$1,在这种情况下,只会包含“hello; ’cruel’world”位于自己的argv位置,*将被替换为当前目录中的文件列表(每个此类文件替换为单独的参数)脚本甚至开始了。因为负责解析用户命令行(运行脚本的shell不一样!)的shell在运行此解析时没有将引号识别为有效,所以在脚本运行时,你无法恢复原始数据。

答案 1 :(得分:1)

摘要:探讨了使用sed更改变量的方法,但您真正需要的是一种使用和编辑文件的方法。它已被覆盖。

桑达

(两个)sed行可以用这个来解决(注意-i不使用,它不是文件而是值):

input='“very dirty”    
we’re'

sed 's/[“”]/\"/g;s/’/'\''/g' <<<"$input"

但是使用shell的内部结构应该更快(对于小字符串):

input='“very dirty”    
we’re'

input=${input//[“”]/\"}
input=${input//[’]/\'}
printf '%s\n' "$input"

$ 1

但是您的脚本存在潜在问题,您正在尝试清除从命令行接收的输入。您正在使用$1作为字符串的来源。有人写道:

./script  “very dirty”    
we’re

输入丢失了。它被分解为贝壳的代币和&#34; $ 1&#34;仅为“very

但我不相信这就是你真正拥有的。

文件

但是,您也说输入来自文件。如果是这种情况,请阅读:

input="$(<infile)"           # not $1

sed 's/[“”]/\"/g;s/’/'\''/g' <<<"$input"

或者,如果您不介意编辑(更改)文件,请改为:

sed -i 's/[“”]/\"/g;s/’/'\''/g' infile
input="$(<infile)"

或者,如果您清楚并确定脚本的内容是文件名,例如:

./script infile

您可以使用:

infile="$1"
sed -i 's/[“”]/\"/g;s/’/'\''/g' "$infile"
input="$(<"$infile")"

其他评论:

然后:

  • 引用您的变量。
  • 请勿使用陈旧的`…`语法,而是使用$(…)
  • 不要在UPPER情况下使用变量,这些变量是为环境变量保留的。
  • 并且(除非你的意思是sh)使用针对bash的shebang(第一行)。
  • 命令enscript最明确地需要一个文件,而不是一个变量。
  • 也许你应该使用evince来打开PS文件,除了你知道你真的需要它之外,不需要制作pdf的步骤。
  • 我认为最好使用一个文件来存储enscript和ps2pdf的输出。
  • 在所有内容都按预期工作之前,不要隐藏命令打印的错误,然后只需将脚本调用为:

    ./ script infile 2&gt; / dev / null

    或根据要求减少冗长。

最终剧本。

如果使用enscript将要使用的文件的名称调用脚本,例如:

./script infile

然后,整个脚本将如下所示(在bash或sh中运行):

#!/usr/bin/env bash
Usage(){ echo "$0; This script require a source file"; exit 1; }
[ $# -lt 1 ] && Usage
[ ! -e $1 ] && Usage
infile="$1"
pdir="$HOME/Desktop"
open_pdf=evince

# Straighten out curly quotes
sed -i 's/[“”]/\"/g;s/’/'\''/g' "$infile"

tmpfile="$(mktemp "$pdir"/mutt_XXXXXXXX.pdf)"
outfile="${tmpfile%.*}.ps"
enscript --font=Courier10 "$infile" -2r \
     --word-wrap --fancy-header=mutt -p "$outfile"

ps2pdf "$outfile" "$tmpfile"

"$open_pdf" "$tmpfile" >/dev/null 2>&1 &
sleep 5
rm "$tmpfile" "$outfile"