当试图将一些(对我来说)常见的bash成语变成乌龟脚本时, 我还是遇到问题。这是一篇很长的帖子,对不起 - 你可能只是跳过了 介绍性解释,并在最后跳到实际问题 - 但我希望以这种方式清楚地表达我的观点(问题)。
我经常在bash脚本中使用的一个成语是链接在一起(管道)find,egrep和xargs 使用以null结尾的字符串。原因很简单:即使是带有空格和其他奇怪的文件名 字符不会以这种方式引起任何问题。
我会用这样的东西:
找到。 -name" *" -print0 ... | egrep -z -Z ... | xargs -0 ...有时我想在与-L 1
匹配的文件上逐行工作 找到。 -name" *" -print0 ... | egrep -z -Z ... | xargs -0 -L 1 ...或者,而不是xargs -0 ...我会使用另一个工具,比如rssh和ssh, 也理解空终止字符串:-0
将当前目录的(基本)内容同步/保存到其他目录 例如。我会使用类似的东西:
binaries="exe$"
logfiles="log$"
pidfiles="pid$"
shakestuff="\_shake|\_build|\.\.database"
pat="^\.$|/dist|\.cabal-sandbox|cabal\.sandbox\.config|$shakestuff|\.o$|\.dyn_o$|\.hi$|\.dyn_hi$|\.hdevtools.sock$|$binaries|$logfiles|$pidfiles|TAGS"
find . -iname "*" -print0 -type f | egrep -z -Z -v "$pat" | rsync -a -e ssh --delete --progress --files-from=- -0 ./ .../path/to/some/other/dir
find打印当前目录中的所有文件,null终止:-print0
egrep -v" $ pat",从这个文件列表中只保留那些与模式$ pat不匹配的文件, 仅限基本文件即:我不打算在.cabal-sandbox中同步/保存文件 目录例如,并且位于该链中间的egrep必须消耗和生产 null终止字符串:-z -Z 图案pat预先组装在一起。
这里指示使用ssh的rsync从stdin获取输入: - files-from = - , 再次null终止:-0(注意,虽然通常" rsync ... from到"表现 根据目录来自下面的斜杠,给出了非常不同的结果, 在这里:./或不,这里不太重要,因为rsync的输入来自stdin: - )
现在我试图把它变成一个乌龟脚本,虽然取得了一些成功,但是我仍然面对着 一些问题,并希望把它变成更惯用的海龟:
为了完整性,这是我目前在文件sync.hs中工作的脚本, 在一个小runturtle bash脚本的帮助下调用, 我可以调用sync.hs
要么只显示正在考虑的文件列表:sync.hs -e
或将它们同步到另一个目录,如:sync.hs --to / path / to / other / dir
这是代码(runturtle):
#!/bin/sh
exec cabal exec runhaskell -- "$@"
这是代码(sync.hs):
#!/usr/bin/env runturtle
{-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE ExtendedDefaultRules #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
import Turtle
data Opts = Opts {
doEcho :: Bool
, toDir :: Turtle.FilePath
}
deriving (Show)
parser :: Parser Opts
parser = Opts <$>
(switch "echo" 'e' "echo the files considered for synchronizing")
<*> (optPath "to" 't' "sync to dir")
binaries="|\\./website$|srv$"
logfiles="|log$"
pidfiles="|pid$|pnm$"
shakestuff="|_shake|_build|\\.\\.database"
pat="^\\.$"
<>"|/dist|\\.cabal-sandbox|cabal\\.sandbox\\.config"
<> shakestuff
<>"|\\.git|\\.o$|\\.dyn_o$|\\.hi$|\\.dyn_hi$|\\.hdevtools.sock$"
<> binaries
<> logfiles
<> pidfiles
<>"|TAGS"
sync :: Opts -> IO ()
sync opts = do {
; echo "syncing..."
; when (doEcho opts)
(do {
; echo $ "pat: " <> pat
; sh $ do inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty
& inproc "egrep" ["-z", "-Z" , "-v", pat]
& inproc "xargs" ["-0", "-L", "1"]
& grep (has ".")
>>= echo
; exit ExitSuccess
})
; do {
; let txt = "find . -iname \"*\" -print0 -type f | egrep -z -Z -v \"" <> pat <>"\" | rsync -a -e ssh --delete --progress --files-from=- -0 ./ "
<> format fp (toDir opts)
; echo txt
; shell txt empty
; return ()
}
; return ()
}
main :: IO ()
main = (do {
; opts <- options "sync file to another directory" parser
; print (opts)
; sync opts
; return ()
})
现在这是我对这个脚本的问题:
首先:我可以在命令行上运行它,我的flycheck语法检查 在emacs依赖ghc其他hdevtools工作正常,从而获得 现在,Haskell的强大打字功能可以为shell编写好处(感谢创建龟 顺便说说)。我甚至可以在命令行上使用乌龟(cabal repl)
cabal repl
> :set -XOverloadedStrings
> import Turtle
> ls "."
> view (shell "whatever cmd" empty)
等,但是如果我加载了我的sync.hs脚本,我就无法访问它的各个部分(函数 同步定义)
> :l sync.hs
[1 of 1] Compiling Main ( sync.hs, interpreted )
Ok, modules loaded: Main.
我希望看到上面定义的模式,例如:
> pat
<interactive>:12:1:
Not in scope: ‘pat’
Perhaps you meant ‘cat’ (imported from Turtle)
我想使用sync.hs中定义的函数作为实验的快捷方式 例如。像这样
> view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty & inproc "egrep" ["-z", "-Z" , "-v", pat]
<interactive>:15:111:
Not in scope: ‘pat’
Perhaps you meant ‘cat’ (imported from Turtle)
其次,你可能已经注意到我在上面的乌龟脚本中已经使用过&#34;更多惯用的&#34; 在回声的情况下龟:
; sh $ do inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty
& inproc "egrep" ["-z", "-Z" , "-v", pat]
& inproc "xargs" ["-0", "-L", "1"]
& grep (has ".")
>>= echo
即。我正在使用乌龟风格的管道:功能应用,这里的顺序与&amp ;, 至少比toDir更真实,我实际上依赖于bash 做这个工作:
; let txt = "find . -iname \"*\" -print0 -type f | egrep -z -Z -v \"" <> pat <>"\" | rsync -a -e ssh --delete --progress --files-from=- -0 ./ "
<> format fp (toDir opts)
; echo txt
; shell txt empty
但即使在这个更为惯用的回声案例中,我也不得不采取一些解决方法:grep(有&#34;。&#34;),如果我不使用它,我会看到空字符串:
turtle> view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty & inproc "egrep" ["-z", "-Z" , "-v", "\\.cabal-sandbox|/dist"]
输出(这里省略了很多输出,但是看到单个&#34; \ NUL&#34;在最后):
"...ntax.hs\NUL./static/lib-pi-forall/src/PiForall/Parser.hs\NUL./static/lib-pi-forall/src/PiForall/TypeCheck.hs\NUL./static/lib-pi-forall/LICENSE\NUL./shclean.do\NUL./TAGS\NUL./T10.hs\NUL./todo-yet-stop-the-program-as-in-running-if-not-told-another\NUL./talks\NUL./index.html\NUL./T1.hs.orig\NUL./sbbuild.sh\NUL./_shake\NUL./_shake/Main.hi\NUL./_shake/Main.dyn_o\NUL./_shake/build\NUL./_shake/Main.o\NUL./_shake/Main.dyn_hi\NUL./T4.hs\NUL./sync.hs\NUL./etc\NUL./.hdevtools.sock\NUL./more-stuff.hs\NUL./my.hs\NUL./T9.hs\NUL./snap-index\NUL./T6.hs\NUL./etc.html\NUL./cabalfile.hs\NUL./todo-maybe-issue-start-stop-restart-july2016\NUL./try-turtle-urwclassico.do\NUL./install.do\NUL./update-rc\NUL./index\NUL./done-pipe\NUL./clean.do\NUL./bootstrap.do\NUL./mystuff.cabal\NUL./pire\NUL./log\NUL./build.sh\NUL./goodsync.hs\NUL./cmds.hs\NUL./LICENSE\NUL./dry.do\NUL./T5.hs\NUL./snap-pire\NUL"
"\NUL"
如果我不愿意用grep删除它们,请查看我最后得到的空字符串(有&#34;。&#34;)
turtle> view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty & inproc "egrep" ["-z", "-Z" , "-v", "\\.cabal-sandbox|/dist"] & inproc "xargs" ["-0", "-L", "1"]
(again lots of output omitted)
"./done-pipe"
"./clean.do"
"./bootstrap.do"
"./mystuff.cabal"
"./pire"
"./log"
"./build.sh"
"./goodsync.hs"
"./cmds.hs"
"./LICENSE"
"./dry.do"
"./T5.hs"
"./snap-pire"
""
""
""
""
turtle>
这是为什么?在bash我不必这样做!任何更好/推荐使用null终止的方法 龟中的字符串?
最后,并非最不重要的是,我无法为另一方提出惯用的海龟解决方案, rsync一段代码。这是一次尝试,但看看会发生什么:一些文件被转移, 但rsync抱怨我当前的dir / home / rx / work / servant /未找到null终止: link_stat&#34; / home / rx / work / servant /#012&#34;失败: (是的:它的名字只是&#34; / home / rx / work / servant /&#34; not&#34; / home / rx / work / servant /#012&#34;)
; view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty
& inproc "egrep" ["-z", "-Z", "-v", pat]
& grep (has ".")
& shell ("rsync -a -e ssh --delete --progress --files-from=- -0 ./ " <> (format fp $ toDir opts))
rx@softland ~/work/servant $ ./sync.hs --to ~/tmp/website_
Opts {doEcho = False, toDir = FilePath "/home/rx/tmp/website_"}
syncing...
building file list ...
rsync: link_stat "/home/rx/work/servant/\#012" failed: No such file or directory (2)
135 files to consider
./
q
8,715 100% 0.00kB/s 0:00:00 (xfr#1, to-chk=95/135)
sync.hs
2,034 100% 1.94MB/s 0:00:00 (xfr#2, to-chk=86/135)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1183) [sender=3.1.1]
ExitFailure 23
rx@softland ~/work/servant $
但实际上我甚至想使用inproc作为rsync片段(有或没有grep(有&#34;。&#34;))
; view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty
& inproc "egrep" ["-z", "-Z", "-v", pat]
& grep (has ".")
& inproc "rsync" ["-a", "-e", "ssh", "--delete", "--progress", "--files-from=-", "-0", "./", format fp $ toDir opts]
rx@softland ~/work/servant $ ./sync.hs --to ~/tmp/website_
Opts {doEcho = False, toDir = FilePath "/home/rx/tmp/website_"}
syncing...
"building file list ... "
rsync: link_stat "/home/rx/work/servant/\#012" failed: No such file or directory (2)
" 0 files...\r 100 files...\r137 files to consider"
"./"
"sync.hs"
"\r 2,053 100% 0.00kB/s 0:00:00 \r 2,053 100% 0.00kB/s 0:00:00 (xfr#1, to-chk=86/137)"
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1183) [sender=3.1.1]
rx@softland ~/work/servant $
提前致谢。