我正在尝试使用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
将整个文件作为变量,而不仅仅是文件的文本。因此,尝试调整文件中的文本将需要一些额外的步骤。我还在学习。
答案 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
作为字符串的来源。有人写道:
./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")"
然后:
`…`
语法,而是使用$(…)
。sh
)使用针对bash的shebang(第一行)。enscript
最明确地需要一个文件,而不是一个变量。在所有内容都按预期工作之前,不要隐藏命令打印的错误,然后只需将脚本调用为:
./ 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"