在shell脚本中,我需要找出一个特定的应用程序是否仍在运行。如果我们的应用程序名称不包含任何Umluts(äöüàéè...),这将是一个简单的任务。我怎样才能可靠地为我的进程“grep”?
shell脚本获取应用程序名称作为参数,在本例中为“amétiq siMed Büro.app”。有几个自定义的副本同时运行,它们的命名不同,脚本应该只检查一个特定的应用程序(通过param获取的应用程序),忽略其他应用程序。
当使用grep作为特定应用程序名称时,完全没有命中(param):
bash> ps ax | grep "amétiq siMed Büro.app"
bash>
太多的点击:
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
当尝试手动缩小grep时,再次没有命中:
bash> ps ax | grep "/[A]pplications/am" | grep "Büro"
bash>
似乎grep在Umlaut字符第一次出现的位置后停止工作。
我也试过lsof
-没有成功。知道下一步该做什么吗
运行OS X 10.7-10.9
3条答案
按热度按时间iyfamqjs1#
tl; dr
pgrep
而不是ps
+grep
iconv -t UTF8-MAC
将搜索字符串转换为NFD(归一化 * 分解 * Unicode)形式。简而言之:* * Mac文件系统(HFS+)以 * 分解的 * Unicode形式(NFD)存储文件名,而你输入到shell的文件名是*composed * Unicode form(NFC)**,**shell和Unix实用程序都不会将两个 * 等价的 * 字符串-相同的 * content 、不同的 * form -视为 * content-equivalent,即使它们 * 应该 *。
如果你对血腥的细节感兴趣,请继续阅读。
背景
一些重音Unicode字符有一个组成形式-一个代码点直接表示字符(例如:
ü
)-以及 * 等价分解形式-基字符后跟一个组合变音字符(例如,u
,后跟¨
);请参阅https://en.wikipedia.org/wiki/Unicode_equivalence了解更多信息。仅包含合成字符的字符串为NFC正常[ized]形式( C * 表示“Composed”),而仅包含分解字符的字符串为NFD正常[ized]形式(* D * 表示“Decomposed”)。
Mac文件系统(HFS+)将文件名存储在NFD(DEcomposed)中,这有以下含义:
echo *.app
)ls
和类似实用程序的输出麻烦且晦涩的变通方法是仅将NFD字符串与NFD字符串匹配,并且仅将NFC字符串与NFC字符串匹配。
要确定给定字符串是NFD还是NFC形式,请使用例如:
ame?M-^Atiq siMed Bu?M-^Hro.app
(实际上,这是ps
报告的内容-尽管它不应该报告)。或者,通过管道连接到
hexdump -C
以查看各个字节值。请注意,
man
关于ps
不能正确显示包含多字节字符的参数列表的注解本身不是真的(至少从OS X 10.9.2开始):NFC字符串 * 被正确打印,而NFD字符串则不是。与pgrep
形成对比,pgrep
可以正确打印NFC * 和 * NFD字符串,但在 * 匹配 * 时无法识别它们的等价性,如上所述。NFC和NFD表单转换
iconv
和UTF8-MAC
编码方案。以下示例使用输入字符串
'ü'
$'\xc3\xbc'
-即字节0xC3 0xBC
,这是Unicode码点0xFC
的UTF8编码$'u\xcc\x88'
-即u
-* 基 * 字符-后跟字节0xCC 0x88
,这是Unicode码点0x308
的UTF8编码,即所谓的组合分块(¨
)。演示转换;注意,在终端中,结果将始终显示为
ü
-pipe tohexdump -C
,例如,查看字节值。使用这些转换是安全的,因为如果输入字符串已经是目标格式,则保持原样。
bash
shell函数quoteNonAscii
;在本例中,要获得 * NFD * 形式的应用程序名称表示:cd
到/Applications
(或应用程序所在的位置)quoteNonAscii am*tiq*siMed*B*ro.app
-* pathname expansion * 将确保glob扩展为文件名的 * NFD * 形式。macOS中的区域设置:
注:这是原始答案的修订后的残余,希望其中仍然包含有用的信息。
locale
可以告诉您哪个区域设置有效,反映在以下环境变量中:LANG
、LC_COLLATE
、LC_CTYPE
、LC_MESSAGES
、LC_MONETARY
、LC_NUMERIC
、LC_TIME
。例如,如果美国英语区域设置生效,您会看到:Terminal.app
和其他终端程序,如iTerm
默认情况下预先配置shell的区域设置,以匹配通过System Preferences > Language & Region
指定的用户区域设置(在Terminal.app
中,您可以通过Preferences... > Settings > {Your Profile} > Advanced
关闭此行为,复选框Set locale environment variables on startup
)。*字符编码(反映在区域设置ID的
.{encoding}
后缀中,通常为.UTF8
)将与终端程序设置中配置的编码匹配(对于Terminal.app
,请转到Preferences... > Settings > {Your Profile} > Advanced
并更改Character encoding
设置),如果支持(使用locale -a
查看所有支持的语言/区域+编码组合)。Terminal
和iTerm
都默认为UTF-8,这是一个明智的选择。Terminal
中将没有编码后缀(例如,只有en_US
),并在iTerm
中完全恢复到"C"
区域设置-并且可能无法正常工作(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_*
类别提供了一个 default,同时允许您选择性地覆盖它们,而export LC_ALL=...
* 覆盖 * 所有LC_*
类别。支持的locale 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实现取代时,这一点发生了变化)。dnph8jn42#
似乎我用osascript/AppleScript解决问题太快了-我可以在终端中过滤我的进程,但由于某种原因,它在我的脚本中不起作用。
所以我找到了解决这个问题的方法:如果我不能使用像ps、lsof、…之类的命令可靠地“grep”应用路径匹配我的脚本得到的路径作为param,然后我只需要在一个新的进程的帮助下重新生成它。
再一次,我的问题简而言之:
我的脚本获取一个应用程序路径作为参数。此路径包含变音。此外,应用程序有几个变体,命名不同,其中几个可能同时运行,但是脚本需要过滤它作为param得到的那个。
使用PS、LSOF等。我得到了乱码的输出,无论我设置了什么语言环境,它都不匹配我的参数:
只要字符串中包含Umlaut,grep就会失败:
我的解决方案是在应用程序包中存在的文件上启动一个“tail &”进程,然后做一些ps,cut和awk,以获得我正在寻找的应用程序的pid:
请注意,我必须将LC_ALL和LANG设置为“en_US.UTF-8”(可能不需要设置其中之一,我没有进一步深入研究这个问题...)。
我知道这只是一个变通办法,如果有一个oneliner会更好...至少这个解决方案对我有用。再次感谢所有参与讨论这个问题的人!
sh7euo9m3#
您必须设置区域设置以匹配口音,例如:
否结果
ps
示例: