Все еще сталкиваются с проблемами при попытке превратить некоторые скрипты bash в черепаху, ноль терминированные строки, в частности находящиеся в виновнике
При попытке превратить некоторые (для меня:) идиомы bash в сценарии черепах, я все еще сталкиваюсь с проблемами. Это длинный пост, извините - вы можете просто пропустить вводные пояснения и перейти к актуальным вопросам ближе к концу - но я надеюсь ясно изложить свою точку зрения (вопросы) таким образом.
Одна идиома, которую я часто использую в сценариях bash, - это связывание воедино (piping) find, egrep и xargs со строкой, заканчивающейся нулем. Причина проста: даже имена файлов с пробелами и другими странными символами таким образом не вызывают проблем.
Я бы использовал что-то вроде этого:
находить. имя "*" -print0 ... | egrep -z -Z ... | xargs -0 ...
Иногда я хотел бы работать построчно над файлами, соответствующими -L 1
находить. имя "*" -print0 ... | egrep -z -Z ... | xargs -0 -L 1 ...
Или вместо xargs -0... я бы использовал другой инструмент, например rsync с 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 печатает все файлы в текущем каталоге, завершается нулем: -print0
egrep -v "$ pat", сохраняет из этого списка файлов только те, которые не соответствуют шаблону $pat, только необходимые файлы, т. е. я не беспокоюсь о синхронизации / сохранении файлов в каталоге.cabal-sandbox, например, и egrep нахождение в середине этой цепочки должно потреблять, а также создавать строки с нулевыми концами здесь: -z -Z Паттерн патт собирается заранее по частям.
rsync с ssh получает указание на получение ввода от stdin: --files-from=-, снова завершается null: -0 (обратите внимание, что в целом "rsync... from to" ведет себя очень по-разному, в зависимости от того, где находится каталог из задается с косой чертой, как здесь: ./ или нет, здесь это менее важно, так как входные данные для rsync поступают из stdin: -)
Теперь я попытался превратить это в сценарий черепахи, но с некоторым успехом, т. Е., Но столкнулся с некоторыми проблемами и хотел бы превратить это в более идиоматическую черепаху:
Для полноты картины, вот мой в настоящее время работающий скрипт в файле sync.hs, который вызывается с помощью небольшого скрипта 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 для скриптов оболочки (спасибо, что, кстати, за создание turtle). Я даже могу использовать черепаху в командной строке (Cabal Repl)
cabal repl
> :set -XOverloadedStrings
> import Turtle
> ls "."
> view (shell "whatever cmd" empty)
и т.д., но если я загружаю свой скрипт sync.hs, я не могу получить доступ к его частям (функции определены в sync)
> :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)
Во-вторых, вы, возможно, заметили в моем скрипте выше, что я использовал "более идиоматическую" черепаху в случае эха:
; sh $ do inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty
& inproc "egrep" ["-z", "-Z" , "-v", pat]
& inproc "xargs" ["-0", "-L", "1"]
& grep (has ".")
>>= echo
то есть. Я использую стиль трубопровода в черепахе: функция приложения, здесь в обратном порядке с &, более идиоматично, по крайней мере, чем в случае 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 (has "."). Если я не использую это, я получаю увидеть пустые строки:
turtle> view $ inproc "find" [".", "-iname", "*", "-print0", "-type", "f"] empty & inproc "egrep" ["-z", "-Z" , "-v", "\\.cabal-sandbox|/dist"]
output (здесь много опущено, но в самом конце смотрите одиночный "\NUL"):
"...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 (has ".")
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 я не обязан это делать! Есть какой-нибудь лучший / рекомендуемый способ использования строк с нулевым символом в черепахе?
И, наконец, что немаловажно, я не смог придумать идиоматическое решение для "черепашек" для другого фрагмента кода rsync. Вот попытка, но посмотрим, что произойдет: некоторые файлы передаются, но rsync жалуется, что мой текущий каталог dir /home/rx/work/servant/ не найден с нулевым окончанием: link_stat "/home/rx/work/servant/#012"не удалось: (ну да: его имя просто"/home/rx/work/servant/" not "/home/rx/work/servant/#012")
; 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 (имеет "."))
; 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 $
Заранее спасибо.