sve*_*son 2 macos bash shell grep
在shell脚本中,我需要找出特定应用程序是否仍在运行.这将是一个简单的任务做,如果我们的应用程序的名称将不包含任何变音(äöüàéè...).我怎么能可靠地"grep"我的问题?
shell脚本在此示例中获取应用程序名称作为参数" amétiqsiMedBüro.app ".有几个自定义副本同时运行,它们的名称不同,脚本应仅检查特定应用程序(通过param获取的应用程序)并忽略其他应用程序.
使用grep作为特定的app-name(param)时根本没有命中:
bash> ps ax | grep "amétiq siMed Büro.app"
bash>
Run Code Online (Sandbox Code Playgroud)
点击次数过多:
bash> ps ax | grep "/[A]pplications/am"
4335 ?? S 5:19.01 /Applications/ame?M^Atiq siMed Bu?M^Hro.app/Contents/MacOS/siMed2
10188 ?? S 0:03.18 /Applications/ame?M^Atiq siMed SUPPORT.app/Contents/MacOS/siMed2
Run Code Online (Sandbox Code Playgroud)
尝试手动缩小grep时再次没有命中:
bash> ps ax | grep "/[A]pplications/am" | grep "Büro"
bash>
Run Code Online (Sandbox Code Playgroud)
似乎grep在第一次出现变音字符的位置后停止工作.
我也尝试过lsof- 没有成功.任何想法接下来要尝试什么?
运行OS X 10.7-10.9
pgrep而不是ps+grepiconv -t UTF8-MAC您的搜索字符串转换为NFD(归分解 Unicode)的形式.pgrep -qlf "$(iconv -t UTF8-MAC <<<'amétiq siMed Büro.app')" && echo "RUNNING"
Run Code Online (Sandbox Code Playgroud)
简而言之:在苹果机文件系统(HFS +)存储在文件名中分解的Unicode形式(NFD) ,而你键入一个外壳是由 Unicode的形式(NFC)和无论是外壳还是Unix工具处理两个等价的字符串-同样的内容,不同的形式 - 内容相同 - 即使他们应该.
如果您对血腥细节感兴趣,请继续阅读.
一些重音的Unicode字符具有组合形式 - 直接表示字符的单个代码点(例如ü) - 以及等效的 分解形式 - 基本字符后跟组合变音字符(例如u,后跟¨); 有关更多信息,请参阅https://en.wikipedia.org/wiki/Unicode_equivalence.
仅包含组合字符的字符串是NFC正常形式(C代表'组合'),而仅包含分解字符串的字符串是NFD正常[形式]形式(D代表'分解').
Mac文件系统(HFS +)在NFD(DEcomposed)中存储文件名,其含义如下:
类似地,在shell(Terminal.app中的bash)中,以下所有技术都会产生NFD字符串:
echo *.app)ls和类似的实用程序相比之下,如果您在shell中键入脚本或应用程序名称(或从其他位置复制NFC表单),它将在NFC中表示.
问题的关键:shell和Unix实用程序无法识别NFD和NFC表单的等价性,因此将它们视为不同.
- 繁琐和模糊 - 解决方法是仅将NFD字符串与NFD字符串匹配,并且仅针对NFC字符串匹配NFC字符串.
该阴险的事情是,给定的字符串的NFD和NFC形式看起来完全相同的壳-因为他们应该-但处理方式不同.
要确定给定字符串是否为NFD或NFC格式,请使用,例如:
cat -v <<<'ame?tiq siMed Bu?ro.app'
Run Code Online (Sandbox Code Playgroud)
ame?M-^Atiq siMed Bu?M-^Hro.app(事实上,这是ps报告的内容 - 尽管它不应该报道).或者,通过管道来hexdump -C查看单个字节值.
请注意,man关于句话ps无法正确显示包含多字节字符的参数列表是不正确的本身(至少OS X 10.9.2的):NFC字符串被正确地打印,而那些NFD不是.与之对比pgrep,可正确打印 NFC 和 NFD字符串,但在匹配时无法识别它们的等效性,如上所述.
iconv与UTF8-MAC编码方案.以下示例使用输入字符串 'ü'
$'\xc3\xbc'即字节0xC3 0xBC,即Unicode代码点的UTF8编码0xFC$'u\xcc\x88'- 即a u- 基本字符 - 后跟字节0xCC 0x88,即Unicode码点的UTF8编码0x308,即所谓的组合diaeresis(¨).证明转换; 请注意,在终端中,结果将始终显示为ü- hexdump -C例如,以查看字节值.
# NFC -> NFD
iconv -t UTF8-MAC <<<$'\xc3\xbc' # -> $'u\xcc\x88'
# NFD -> NFC
iconv -f UTF8-MAC <<<$'u\xcc\x88' # -> $'\xc3\xbc'
Run Code Online (Sandbox Code Playgroud)
这些转换可以安全使用,如果输入字符串已经是目标格式,则保持原样.
bashshell函数quoteNonAscii; 在手头的情况下,以NFD形式表示应用程序名称:
cd到/Applications(或申请所在的任何地方)quoteNonAscii am*tiq*siMed*B*ro.app- 路径名扩展将确保glob扩展为文件名的NFD形式.# Pass any string to this function to output
# an ANSI-C-quoted string with all non-ASCII bytes represented
# as \x{nn} hex. codes; trailing newlines are always trimmed.
# Examples:
# quoteNonAscii 'ü' # (if NFC) -> $'\xc3\xbc'
# quoteNonAscii 'u?' # (if NFD) -> $'u\xcc\x88'
quoteNonAscii() {
hexdump -ve '/1 "%02x "' <<<"$*" |
awk -v RS=' ' '
BEGIN { printf "$\x27" } # print the opening of the ANSI-C-quoted string, `${single quote}`
$1=="0a" { nls=nls "\x5cn"; next } # store consecutive newlines in a temp. variable
nls { printf "%s", nls; nls="" } # a non-newline char; we now know that the newlines stored so far are NOT trailing, so we print them and clear the temp. variable.
$1>"7f" { printf "\\x" $1; next } # a non-ASCII byte -> PRINT AS `\xnn`
$1=="22" { printf "\x5c\x22"; next } # a double-quote char. -> escape with `\`
$1=="27" { printf "\x5c\x27"; next } # a single-quote char. -> escape with `\`
$1=="07" { printf "\\a"; next } # bell char.
$1=="08" { printf "\\b"; next } # backspace
$1=="09" { printf "\\t"; next } # tab
$1=="0b" { printf "\\v"; next } # vertical tab
$1=="0c" { printf "\\f"; next } # ff
$1=="0d" { printf "\\r"; next } # CR
$1=="1b" { printf "\\e"; next } # escape
{ system("printf %b \"\\x" $1 "\"") } # a byte that is an ASCII char -> print as a CHAR.
END { print "\x27"}' # print the closing `{single quote}` of the ANSI-C-quoted string.
}
Run Code Online (Sandbox Code Playgroud)
注意:这是原始答案中的修订残余,希望仍然包含有用的信息.
locale在一个交互的shell告诉你什么是语言环境的影响,体现在以下环境变量:LANG,LC_COLLATE,LC_CTYPE,LC_MESSAGES,LC_MONETARY,LC_NUMERIC,LC_TIME.例如,如果美国英语语言环境有效,您会看到:LANG="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_CTYPE="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_ALL=
Run Code Online (Sandbox Code Playgroud)
默认情况下,默认情况下Terminal.app和其他终端程序(例如iTerm默认情况下)会预先配置shell的语言环境以匹配用户的语言环境(通过System Preferences > Language & Region,Terminal.app可以通过Preferences... > Settings > {Your Profile} > Advanced复选框关闭此行为Set locale environment variables on startup).
的字符编码 -反映在.{encoding}在区域ID后缀,典型地.UTF8-将匹配在终端程序的设置中配置的编码(对于Terminal.app,转到Preferences... > Settings > {Your Profile} > Advanced并改变 Character encoding设置),如果支持的(使用locale -a以查看所有支持的语言/区域+编码组合).
双方Terminal并iTerm 默认为UTF-8 ,这是一个明智的选择.
如果你的终端程序被配置为使用不支持的字符编码,报道区域ID将没有编码后缀(如,只是en_US在)Terminal,并恢复到"C"在完全的语言环境iTerm-和东西可能会无法正常工作(Terminal仍然可以让你打印非-ASCII来自该编码的字符,但实用程序不会将它们识别为字符,从而导致illegal byte sequence错误).
System Preferences(例如,将"German"(de)与"United States"(US)组合在一起,从而产生支持的区域设置de_US),则只会LC_TYPE匹配您的终端程序的编码,并且其他LC_*类别将默认为"C".如果您需要手动设置区域设置,请运行:
export LANG={localeId} 要么export LC_ALL={localeId}区别在于为所有类别export LANG=...提供默认值,LC_*同时允许您有选择地覆盖它们,而export LC_ALL=... 覆盖所有LC_*类别.
支持的区域设置ID可以列出locale -a; 最好选择一种基于UTF-8的产品,例如de_CH.UTF-8.
在POSIX的语言环境 -基本上是一个ASCII-只有美国英语语言环境-既可以通过选择"POSIX"或"C".
警告:macOS附带的所有Unix实用程序都会遇到上述问题:它们无法识别NFC和NFD中的等效Unicode字符串.除了这个问题,许多(但不是所有)Unix实用程序原则上都是UTF8多字节字符感知.
awk; 在早期的macOS版本sort中也不支持UTF8(当先前使用的过时GNU实现被最近的BSD实现替换时,这种情况发生了变化).