Bash 将单个文件脚本分解为具有适当目录布局的项目

Bash 将单个文件脚本分解为具有适当目录布局的项目,bash,Bash,问题 假设我写了一篇长篇大论 用某种语言“朗”, 现在要转换这个单文件脚本 进入包含多个文件的项目的目录树。我想在该文件中插入某种分隔符和文件路径,并以某种方式对其进行处理,以便最终获得: 正确的项目目录布局 生成定义文件 自述文件 main/src和test/src等的单独子目录 例如,给定以下脚本(伪代码): 假设我可以在该文件中插入任意分隔符、命令、转义序列和文件路径,我希望获得以下项目: project/ |-- build.txt |-- notes | `-- note_01

问题

假设我写了一篇长篇大论 用某种语言“朗”, 现在要转换这个单文件脚本 进入包含多个文件的项目的目录树。我想在该文件中插入某种分隔符和文件路径,并以某种方式对其进行处理,以便最终获得:

  • 正确的项目目录布局
  • 生成定义文件
  • 自述文件
  • main/src
    test/src
    等的单独子目录
例如,给定以下脚本(伪代码):

假设我可以在该文件中插入任意分隔符、命令、转义序列和文件路径,我希望获得以下项目:

project/
|-- build.txt
|-- notes
|   `-- note_01.txt
|-- readme.md
`-- src
    |-- main
    |   `-- lang
    |       `-- proj
    |           |-- A.lang
    |           `-- B.lang
    `-- test
        `-- lang
            `-- proj
                `-- MySpec.lang

编辑:

下面是一个不太复杂的版本


我尝试过的

下面是一个简单的方法:

  • 通过在
    #之前加上前缀,将原始脚本转换为bash脚本/bin/bash
  • 将源代码拆分为多个代码
  • 必要时插入包声明
  • 在HEREDOC片段之间添加一束
    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 ../../..
#/bin/bash
mkdir项目
cd项目

cat在我看来,您正在尝试编写自定义解析器。如果您提到的所有块都以双线结尾,这将对您有所帮助

#!/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/示例所示

上述代码的输出是

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,然后无法cd到它。 ->用目录名定义变量,并检查它们是否已经存在

    路径1=src/main/lang/some 如果[-d$path1];然后 做点什么 fi

  • 无法将整个树构造作为单个事务处理。。。 ->写入您创建的每个新目录/文件的文件路径,并在必要时使用它进行还原

    • (我自己的答案)


      考虑以下一种小型嵌入式域特定语言的定义,该语言用于定义带有文本文件的目录树:

      #!/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
      s进入该目录,它将使用任意相对路径。
      save
      命令将此处文档的文本内容保存到文件中。
      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
      

      bash
      -脚本非常紧密地反映了树结构,并且不可能将
      cd../../../../../../../../../../../code>命令弄乱。尽管如此,它仍然缺少各种理想的属性(不是事务性的,没有干运行功能)。

      为什么不使用
      tar
      文件?或者,如果它必须是一个shell脚本,您的意思是手动将原始文本文件转换为tar,然后将其解压?不知怎的,我没有想到。。。我必须先查看一下
      tar
      的内部格式,然后才能判断这个建议是天才还是疯狂,或者两者兼而有之:)从未听说过
      shar
      ,现在正在阅读它。一个
      tar
      文件就是您正在寻找的原始文本文件(假设所有存档成员本身都是文本文件)。(shar
      shar
      文件也是如此,更是如此。)不要重新发明轮子。@melpomene我已经看过shar
      shar
      。它似乎只对“目录树->文件”方向有用。如果我一开始没有树,我在提案中写下的内容似乎已经是生成树的最简单的“shar”。Shar是一种压缩现有树的方法,而不是一种描述新目录树的舒适DSL。所以,这不是我所希望的。不过还是要谢谢你。@chepner可能是我没有正确使用它,但是(未压缩)tar的内部格式看起来并不是在文本文件中定义新目录树的最合适的方式。也许我表达得不够清楚:问题不在于从现有树创建存档,而在于一种人类可读的格式,用于从头开始用文本文件定义新树。我已经更新了我的问题,希望现在能更清楚。谢谢@LuisMuñoz的回复。我希望我可以避免为此编写任何解析器。从我的解决方案中可以看出,将文件拆分为块根本不是问题,您可以在任何需要的地方插入任意分隔符(我不想插入
      我在我的示例中提到编写小脚本来替换
      echo
      命令。您可以在那里创建目录。上面脚本的优点是您不必更改源脚本。对于每种类型的自定义脚本,您不必在diirectories上来回移动。每个脚本都会手动执行。)le它自己的类型。更改脚本不是问题,因为复制它很简单。关于
      echo
      s:好的,是的,我可以用
      cat
      -语句替换
      echo
      -语句,就像在我建议的sol中一样
      #!/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