问题
假设我编写了一个冗长的脚本 用某种语言" lang", 现在想要转换这个单文件脚本 到包含许多文件的项目的目录树中。我想在这个文件中插入一些分隔符和文件路径,并以某种方式处理它,以便最终获得:
main/src
和test/src
等例如,给定以下脚本(伪代码):
// required dependencies, should be moved
// into the build definition build.foo
require "org.foo" % "foo-core" % "1.2.3"
require "org.bar" % "bar-gui" % "3.2.1"
// A longer comment that should be converted
// into a text file and moved into a 'notes'
// subdirectory
/*
#README
Another lengthy comment that should go into
a readme.md
*/
/** A class that should
* go to src/main/lang/proj/A.lang
*/
class A {
def a = "foo"
}
/** Another class
* that should go to src/main/lang/proj/B.lang
*/
class B {
def b = "bar"
}
/** Some tests,
* should end up in
* src/test/lang/proj/MyTest.lang
@Test def testFoo() {
assert(2 + 2 == 5)
}
并假设我可以将任意分隔符,命令,转义序列和文件路径插入此文件,我想获得以下项目:
project/
|-- build.txt
|-- notes
| `-- note_01.txt
|-- readme.md
`-- src
|-- main
| `-- lang
| `-- proj
| |-- A.lang
| `-- B.lang
`-- test
`-- lang
`-- proj
`-- MySpec.lang
修改:
的不太复杂的版本我尝试了什么
这是一种天真的方式:
#!/bin/bash
mkdir -p
和cd
cat
HEREDOC分成适当命名的文件对于上面的脚本,它可能看起来像这样:
#!/bin/bash
mkdir project
cd project
cat <<'EOF' > build.txt
// required dependencies, should be moved
// into the build definition build.foo
require "org.foo" % "foo-core" % "1.2.3"
require "org.bar" % "bar-gui" % "3.2.1"
EOF
mkdir notes
cd notes
cat <<'EOF' > note_01.txt
// A longer comment that should be converted
// into a text file and moved into a 'notes'
// subdirectory
EOF
cd ..
cat <<'EOF' > readme.md
/*
#README
Another lengthy comment that should go into
a readme.md
*/
EOF
mkdir -p src/main/lang/proj
cd src/main/lang/proj
cat <<'EOF' > A.lang
package proj
/** A class
* that should go to src/main/lang/proj/A.lang
*/
class A {
def a = "foo"
}
EOF
cat <<'EOF' > B.lang
package proj
/** Another class
* that should go to src/main/lang/proj/B.lang
*/
class B {
def b = "bar"
}
EOF
cd ../../..
mkdir -p test/lang/proj
cd test/lang/proj
cat <<'EOF' > MySpec.lang
package proj
/** Some tests,
* should end up in
* src/test/lang/proj/MyTest.lang
@Test def testFoo() {
// this should end up in test
assert(2 + 2 == 5)
}
EOF
cd ../../..
这种方法有什么问题
它确实生成了正确的树,但这种方法似乎容易出错:
cd ../../..
对错误的嵌套级别来说太容易了mkdir
来说太容易了,然后无法cd
进入它。 我当然可以尝试通过定义来减少它的脆弱性
mkdir
和cd
一起使用的特殊功能,以及
然后将这些函数的调用与cat
一起包装进去
(mkdirAndCd d ; cat)
等。
但它感觉不太对劲。难道不是那么简单
这样做的方法?可以以某种方式组合标准的bash / linux实用程序
变成一个小小的&amp;非常有限的域特定语言
生成带有文本文件的目录树?也许是一些较新版本的split
,其中一个可以指定拆分的位置以及放置碎片的位置?
相关问题:
mkdir
and touch
in single command tree
- reconstruct file and directory structure from text file contents? 其他有趣的建议似乎不起作用:
shar
。由于shar
使用bash本身来提取存档,因此我的上述提议原则上是以非常罕见的格式手动生成的存档,因此shar
似乎与上述共享所有缺点提案。我宁愿选择更受限制的东西,这样可以做更少的事情,但可以更好地保证结果的质量。也许我应该再次强调,我没有一棵树可以开始,所以没有什么可以压缩的。我只有一个脚本文件,并且大致了解树最终应该是什么样子。
答案 0 :(得分:1)
在我看来,您正在尝试编写自定义解析器。如果您提到的所有块都以双行结尾结束,这可以帮助您
#!/bin/bash
gawk 'BEGIN{RS="\n\n([/][*]|[/]{2,2})"}
{
if ($0 ~ /#README/){
system("echo -e \"\nThis is a Readme.md\n--------\n" $0 "\"")
}else if ($0 ~ /class /){
system("echo -e \"\nThis is a class\n---------\n/*" $0 "\"")
}else if ($0 ~ /require /){
system("echo -e \"\nthis is a conf\n-----------\n" $0 "\"")
}else if($0 ~ /[/]{2,2}.*\n[/]{2,2}/){
system("echo -e \"\nthis is a note\n-----------\n" $0 "\"")
}
}' your_script.lang
关键部分是记录分隔符RS,它分割以&#39; \ n \ n //&#39;开头的代码块。或&#39; \ n \ n / *&#39;。
您可以为每种类型的块编写自定义脚本,而不是echo -e
。
请注意,记录分隔符不会出现在$ 0上,因此您必须添加缺少的字符,如上面的/ class /示例所示。
上述代码的输出是
this is a conf
-----------
// required dependencies, should be moved
// into the build definition build.foo
require org.foo % foo-core % 1.2.3
require org.bar % bar-gui % 3.2.1
this is a note
-----------
A longer comment that should be converted
// into a text file and moved into a 'notes'
// subdirectory
This is a Readme.md
--------
#README
Another lengthy comment that should go into
a readme.md
*/
This is a class
---------
/** A class that should
* go to src/main/lang/proj/A.lang
*/
class A {
def a = foo
}
This is a class
---------
/** Another class
* that should go to src/main/lang/proj/B.lang
*/
class B {
def b = bar
}
关于您的疑虑:
它很容易cd ../../ ..到错误的嵌套级别 - &GT;用根路径定义变量并cd到它。
使用错误的名称mkdir太容易了,然后无法进入它。 - &GT;使用目录名定义变量并检查它们是否已存在。
PATH1 = SRC /主/郎/一些 if [-d $ path1];然后 做一点事 网络
无法将整个树构造作为单个事务处理...... - &GT;写入您创建的每个新目录/文件的文件路径,并在必要时使用它来还原。
答案 1 :(得分:0)
(我自己的回答)
考虑以下用于定义具有文本文件的目录树的微小嵌入式域特定语言的定义:
#!/bin/bash
enter() {
local subdir="$1"
if [ ! -d "$subdir" ]
then
mkdir -p "$subdir"
fi
pushd "$subdir" > /dev/null
}
leave() {
popd > /dev/null
}
save() {
local fileName="$1"
cut -d'|' -f2- > "$fileName"
}
如果需要,enter
命令会创建一个目录,并且cd
进入此目录,它可以使用任意相对路径。 save
命令将here-document的文本内容保存到文件中。 leave
命令将更改为上一个目录。
保存文件时,每行都会删除边距(空格后跟“|”)。这是为了确保脚本的缩进不会干扰写入文件的缩进。
如果这些定义是source
d,那么树生成脚本可以写成如下:
#!/bin/bash
source explode.sh
mkdir project
cd project
enter "src"
enter "main/lang/proj"
save "A.lang" <<'____EOF'
|package proj
|
|/** A totally useful class
| * that should go to src/main/lang/proj/A.lang
| */
|class A {
| def a = "foo"
|}
____EOF
save "B.lang" <<'____EOF'
|package proj
|/** Another very useful class
| * that should go to src/main/lang/proj/B.lang
| */
|class B {
| def b = "bar"
|}
____EOF
leave
enter "test/lang/proj"
save "MyTest.lang" <<'____EOF'
|package proj
|
|/** A test that should end up in
| * src/test/lang/proj/MyTest.lang
|@Test def testFoo() {
| assert(2 + 2 == 5)
|}
____EOF
leave
leave
save "build.txt" <<'EOF'
|require "org.foo" % "foo-core" % "1.2.3"
|require "org.bar" % "bar-gui" % "3.2.1"
EOF
enter "notes"
save "note_01.txt" <<'__EOF'
|A longer comment that should be converted
|into a text file and moved into a 'notes'
|subdirectory. This is a very long comment
|about the purpose of the project. Blah
|blah blah.
__EOF
leave
save "README.md" <<'EOF'
|#README
|
|This is a readme file for my awesome project.
|It ends with this line. Bye.
EOF
执行时,脚本会生成以下目录树:
project/
├── build.txt
├── notes
│ └── note_01.txt
├── README.md
└── src
├── main
│ └── lang
│ └── proj
│ ├── A.lang
│ └── B.lang
└── test
└── lang
└── proj
└── MyTest.lang
bash
- 脚本非常密切地镜像树结构,并且不可能弄乱cd ../../../../../..
命令。它仍然缺乏各种理想的特性(不是交易性的,没有干运行能力)。