将单个文件脚本分解为具有正确目录布局的项目

时间:2018-02-28 19:15:54

标签: bash

问题

假设我编写了一个冗长的脚本 用某种语言" lang", 现在想要转换这个单文件脚本 到包含许多文件的项目的目录树中。我想在这个文件中插入一些分隔符和文件路径,并以某种方式处理它,以便最终获得:

  • 正确的项目目录布局(sth. like this)
  • 构建定义文件,
  • readme' s,
  • main/srctest/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

修改:

以下是own answer below

的不太复杂的版本

我尝试了什么

这是一种天真的方式:

  • 通过预先#!/bin/bash
  • 将原始脚本转换为bash脚本
  • 将源代码拆分为HEREDOCS
  • 在必要时插入包声明
  • 在HEREDOC-pieces
  • 之间添加一堆mkdir -pcd
  • 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进入它。
  • 无法将整个树构造作为单个处理 事务,也就是说,如果脚本中的某些内容失败, 没有简单的方法来清理之前产生的混乱 发生了错误。

我当然可以尝试通过定义来减少它的脆弱性 mkdircd一起使用的特殊功能,以及 然后将这些函数的调用与cat一起包装进去 (mkdirAndCd d ; cat)等。

但它感觉不太对劲。难道不是那么简单 这样做的方法?可以以某种方式组合标准的bash / linux实用程序 变成一个小小的&amp;非常有限的域特定语言 生成带有文本文件的目录树?也许是一些较新版本的split,其中一个可以指定拆分的位置以及放置碎片的位置?

相关问题:

其他有趣的建议似乎不起作用:

  • 使用焦油。这意味着必须手动将文本文件转换为有效的tar-archive。虽然tar存档确实 是一个纯文本文件,但它internal format对于这样一个简单的任务来说看起来不是最舒服的DSL。它从未打算以这种方式直接被人类使用。
  • 类似的论点适用于shar。由于shar使用bash本身来提取存档,因此我的上述提议原则上是以非常罕见的格式手动生成的存档,因此shar似乎与上述共享所有缺点提案。我宁愿选择更受限制的东西,这样可以做更少的事情,但可以更好地保证结果的质量。

也许我应该再次强调,我没有一棵树可以开始,所以没有什么可以压缩的。我只有一个脚本文件,并且大致了解树最终应该是什么样子。

2 个答案:

答案 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 ../../../../../..命令。它仍然缺乏各种理想的特性(不是交易性的,没有干运行能力)。