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

问题描述 投票:3回答:2

问题

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

  • 一个合适的项目目录布局(sth. like this)
  • 构建定义文件,
  • 自述的,
  • 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片之间添加一堆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的特殊函数来使它变得不那么脆弱,然后将这些函数的调用与cats一起包装到(mkdirAndCd d ; cat)等。

但它感觉不太对劲。是不是有一个更简单的方法呢?可以以某种方式将标准的bash / linux实用程序组合成一个微小的,非常受限制的域特定语言,用于生成带有文本文件的目录树吗?也许是一些较新版本的split,其中一个可以指定拆分的位置和放置件的位置?


相关问题:


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

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

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

bash
2个回答
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,它分割以'\ n \ n //'或'\ n \ n / *'开头的代码块。您可以为每种类型的块编写自定义脚本,而不是echo -e。请注意,记录分隔符不会出现在$ 0上,因此您必须添加缺少的字符,如上面的/ class / example中所示。

上面代码的输出是

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 ../../ ..到错误的嵌套级别太容易了 - >用根路径定义变量并cd到它。
  • mkdir太容易用错误的名字,然后无法进入它。 - >使用目录名定义变量并检查它们是否已存在。 path1 = src / main / lang / some if [-d $ path1];然后do_something fi
  • 无法将整个树构造作为单个事务处理... - >写入您创建的每个新目录/文件的文件路径,并在必要时使用它来还原。

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命令会创建一个目录,并且cds会进入此目录,它可以使用任意相对路径。 save命令将here-document的文本内容保存到文件中。 leave命令更改为上一个目录。

保存文件时,每行都会删除边距(空格后跟“|”)。这是为了确保脚本的缩进不会干扰写入文件的缩进。

如果这些定义是sourced,那么树生成脚本可以写成如下:

#!/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 ../../../../../..命令。它仍然缺乏各种理想的属性(不是事务性的,没有干运行能力)。

© www.soinside.com 2019 - 2024. All rights reserved.