Bash脚本创建HTML导航菜单

4

一段时间以前,我编写了一个JavaScript程序来遍历目录树,并从树的前三个级别中构建HTML导航菜单文件。现在我正在尝试使用Bash来复制这个程序,因为JS程序需要IE浏览器和ActiveX才能运行。我对Bash相当陌生,所以这对我来说是一个很好的学习经验。

所以,我有一个如下结构的目录:

-Folder A
    --Folder B
    --Folder C
         --Folder C1
         --Folder C2
               --Folder CC1
    --Folder D
         --Folder D1
    --Folder E

等等,我明白了。无论如何,文件夹名称有所不同,但每个文件夹中都有一个名为“foldername.txt”的文本文件。在这个文件中,有一行文本,显示出菜单中要使用的实际文件夹名称(这是由于某些名称的长度)。

因此,我正在尝试循环遍历每个文件夹/子文件夹,仅到第3级,读取文件夹中的每个foldername.txt文件并返回名称,同时保持文件夹层次结构。我希望这样讲清楚了。输出附加了HTML标签,并回显到.htm文件中。

到目前为止,我已经尝试了不同的方法。下面的代码几乎做到了我想要的事情,它将按照文本文件返回名称扫描目录,但未保持层次结构。不幸的是,我没有包括-maxdepth的find版本。正如你所看到的,我尝试了嵌套循环,因为它只有三层深度,但递归会继续每个级别,以便我得到重复和奇怪的结果。

#!/bin/bash

ROOT=/data/
OUTPUTFILE=${ROOT}/Menu-test.html

# Create first level items - these are static
HEADING="<UL class=navlist1>
<LI><SPAN class=plus><p>-</p></SPAN><A class=''>Level 1 products</A></LI>"

END="</UL>"
L2="<UL class=navlist2>"
L3="<UL class=navlist3>"
LI="<LI><SPAN class=plus><P>+</P></SPAN>"
LIEND="</LI>"

echo $HEADING > $OUTPUTFILE;

# set shell options
shopt -s nullglob

# loop through top level dir
for d in $DIR/*/
do
    for file in $(find $d -name "foldername.txt");
    do
        OUT=$(awk '{ print $0 }' $file)
        echo $LI$OUT$LIEND >> $OUTPUTFILE; 
    done

    # loop through second level dir
    for e in $d/*/
        do
            echo $L2 >> $OUTPUTFILE;
            for file2 in $(find $e -type f -name "foldername.txt");
                do
                    OUT2=$(awk '{ print $0 }' $file2)
                    echo $LI$OUT2$LIEND >> $OUTPUTFILE; 
                done
            echo $END >> $OUTPUTFILE;

                # loop through third level dir
                for f in $e/*/
                    do
                        echo $L3 >> $OUTPUTFILE;
                        for file3 in $(find $f -type f -name "foldername.txt");
                            do
                                OUT3=$(awk '{ print $0 }' $file3)
                                echo $LI$OUT3$LIEND >> $OUTPUTFILE; 
                            done
                        echo $END >> $OUTPUTFILE;
                    done
        done
done
echo $END >> $OUTPUTFILE;

抱歉,这篇文章很长,代码也有点乱,但我真的想自己试着写一下,因为这是我学习的最佳方式。所以你们有什么关于如何让这个工作的想法吗?请注意,我没有使用Python或任何其他语言,所以只能使用bash。

我需要的输出结果应该像下面这样(连字符只是为了保持格式,不在输出中):

**<LI><SPAN class=plus><P>+</P></SPAN><A href=''>**
<UL class='navlist1'> 
    <LI><SPAN class='plus'><p>-</p></SPAN><A class=''>Folder A</A> 
        <UL class='navlist2' style='display:block'> 
            <LI><SPAN class='bull'><p class='bull'>&bull;</p></SPAN><A href='http://www.somewhere.com/index.htm'>Folder A1</A></LI> 
            <LI><SPAN class='bull'><p class='bull'>&bull;</p></SPAN><A href='http://www.somewhere.com/index.htm'>Folder A2</A></LI> 
            <LI><SPAN class='bull'><p class='bull'>&bull;</p></SPAN><A href='http://www.somewhere.com/index.htm'>Folder A3</A></LI> 
        </UL**></A>
    <UL class=navlist1>**
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder B</A></LI>
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder C</A></LI>
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D</A></LI>
           <UL class=navlist2>
                <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1</A>
                    <UL class=navlist3>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1A</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1B</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1C</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1D</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D1E</A></LI>
                    </UL>
                </LI>
                <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D2</A>
                    <UL class=navlist3>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D2A</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D2B</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder D2C</A></LI>
                    </UL>
                </LI>
            </UL>
        </LI>
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder E</A></LI>
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder F</A></LI>
        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G</A></LI>
            <UL class=navlist2>
                <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G1</A>
                    <UL class=navlist3>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G1A</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G1B</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G1C</A></LI>
                        <LI><SPAN class=plus><P>+</P></SPAN><A href=''>Folder G1D</A></LI>
                    </UL>
                </LI>
            </UL>
        </LI>
    </UL>
**</LI>**

所以,这是目前的工作输出。我还需要在每个标签中包含href链接,但是每个子文件夹的级别将具有不同的路径,如下所示:
navlist1 =
http://www.somewhere.com/here//landing.htm
navlist2&3 = http://www.somewhere.com/here/there//index.htm
“folder”是目录的实际文件夹名称,而不是文本文件中的名称,否则链接将无法使用。
上述加粗的行不应出现在输出中。
谢谢。

很遗憾,我没有包含-maxdepth的find版本。在这种情况下,您可以使用构造for VAR in $DIR/*/*/*,它返回最多三级深度的所有文件+路径。这将大大减少您的代码量。 - thom
谢谢。我最初是用“for d in $DIR///*/”开始的,感谢这个论坛的成员,但我无法得到我想要的结果,所以认为嵌套会是更好的方法。显然不是。 - Longman
Bash非常强大,具有条件语句和字符串替换功能。很可能您最初的想法使用$DIR///*已经接近您想要的结果,只需要进行一些微调和少量的Bash魔法即可。如果您再次尝试,我可以帮助您完成它,如果您愿意的话。 - thom
可能更容易调整您的JavaScript程序以适应不同的解释器,例如Rhino或Node.js。 - glenn jackman
谢谢大家,不幸的是,由于系统设置的方式,JS不是一个选项(很遗憾),这就是为什么我正在尝试在bash中实现它。Thom - 谢谢,我可以使用那种方法获取所有文件夹名称等,但我无法弄清楚如何在保持逻辑文件夹结构的同时分离所有内容。我希望输出看起来像标准的HTML <UL>列表。 - Longman
显示剩余3条评论
3个回答

3
也是一个递归的例子(我忍不住了,抱歉);-)
#!/bin/bash

#preset variables, exec redirects everything to outputfile
ROOT="/data"
LABEL="foldername.txt"
MAXDEPTH=5
DEPTH=0
HTTP="http://www.somewhere.com"
exec > "$ROOT/Menu-test.html"

#functions for indentation, definition and printing tags
LI="<LI><SPAN class=plus><P>+</P></SPAN>"
ULecho() { Dent ; echo "<UL class='navlist$DEPTH'>"                    ;}
LIecho() { echo -n "$LI<A href='$HTTP${1/$ROOT/}/'>$( cat $LABEL)</A>" ;}
Indent() { for (( i=1 ; i < DEPTH ; ++i )); do Dent; Dent; done ; Dent ;}
Dent()   { echo -n "    "                                              ;}
LIstrt() { Indent; LIecho "$( pwd )" ; echo "</LI>"                    ;}
ULstrt() { Indent; LIecho "$( pwd )" ; echo; Indent; ULecho            ;}
TAGend() { Indent ; Dent ; echo "</UL>"; Indent; echo "</LI>"          ;}
DEPchk() { [ "$DEPTH" -gt "0" ] && ${1} ;}

:> $ROOT/$LABEL

Dive()
{
    local DPATH="$1"


    if [ "$( echo */$LABEL )" = "*/$LABEL" ] || [ $DEPTH -gt $MAXDEPTH ]
    then
        DEPchk LIstrt
    else
        DEPchk ULstrt
        for DPATH in */$LABEL
        do
            cd ${DPATH%/*}
              (( ++DEPTH ))
            Dive "$DPATH"
              (( --DEPTH ))
            cd ..
        done
        DEPchk TAGend
    fi
}

cd $ROOT
Dive "$ROOT"
echo "</UL>"

更新:
我尝试添加你提到的一些额外内容,但我认为我对此没有详细的了解。可能是因为我的HTML知识几乎不存在。所以你真的需要给我讲清楚;-)


哇,Thom,我真的感激不尽,这太棒了,代码量很少,而用JavaScript则需要大约40行代码。它几乎完成了。<UL>构建得非常完美。有几个问题。我已经使用您的fantastic函数更新了我的问题示例。正如您所注意到的,输出的第一行不应该存在。'navlist1'是静态的,不是目录结构的一部分,并且作为变量单独添加,我应该提到这一点。因此,输出的第一行应该是navlist1。因此,在第9行的navlist1根本不应该存在。 - Longman
我认为这个更新会接近你所想要的。 - thom
谢谢 Thom,这绝对是太棒了,同时也教会了我很多关于BASH的知识,我感激不尽。HTML部分非常完美,只需要进行一些小的调整即可。现在唯一需要做的就是弄清楚如何在第一层文件夹中添加一个不同的URL,因为那些链接将指向着陆页面,而下面的所有内容都将指向不同位置的索引页。我将尝试在$DEPTH变量周围添加条件语句。再次感谢! - Longman
嗨,汤姆,一切都完成了,我得到了我需要的东西,并解决了可变的URL问题。非常感谢您的帮助,您在短时间内教给了我很多东西。 - Longman
这是个好消息 :-) 谢谢你的赞美之词。我很高兴能够帮助。这是一个有趣的项目。随时欢迎你的到来。 - thom
显示剩余2条评论

1
迭代而非递归,实际上并非计划中的方式,但这似乎是一种有趣的方法。 看起来有点凌乱,对此我感到抱歉。
顺便说一句,我也忍不住做了一个递归版本,它看起来更整洁
(代码更少,请查看我的其他答案。)
#!/bin/bash

#preset variables, exec redirects everything to outputfile
ROOT="/data"
exec > "$ROOT/Menu-test.html"
MAXLEVEL="*/* */*/* */*/*/* */*/*/*/*"
LABEL="foldername.txt"
NAVLIST=1
LEVEL=2
LAST=1

#functions for indentation, definition and printing tags
LI="<LI><SPAN class=plus><P>-</P></SPAN><A class=''>"
Indent() { for (( i=1 ; i < LAST ; ++i )); do echo -n "        " ; done ;}
BUecho() { Indent ; echo "<UL class=navlist""$LEVEL"">"                 ;}
EUecho() { Indent ; echo "</UL>"                                        ;}
BLecho() { Indent ; echo -n "    " ; echo "$LI""$DIRNAME""</A>"         ;}
ELecho() { Indent ; echo -n "    " ; echo "</LI>"                       ;}

# Create first level items - these are static
cat<<EOH
<UL class=navlist1>
    <LI><SPAN class=plus><p>-</p></SPAN><A class=''>Level 1 products</A>
EOH

#go to root of tree, scan tree, drop folders without label,drop labels,sort
#and start reading the result line by line .... 
cd "$ROOT" ; for SCANDIRS in $MAXLEVEL; do echo "$SCANDIRS"; done |\
grep "$LABEL" |sed "s/$LABEL/\//g" |sort |\
while [ "$LAST" -gt "0" ]
do
    #read directory entry, count slashes as depth indicator, read label
    if read LINE
    then
        LEVEL=$( tr -dc '/' <<<"$LINE" | wc -m )
        read DIRNAME < "$LINE/$LABEL"
    else
        LINE="" ; LEVEL=0
    fi

    #code logic, assembling tags
    if    [ "$LEVEL" -gt "$LAST" ];   then (( ++LAST )); BUecho; BLecho
    elif  [ "$LEVEL" -eq "$LAST" ];   then  ELecho; BLecho
    else
        while [ "$LEVEL" -lt "$LAST" ]; do  ELecho; EUecho; (( --LAST ));
        done; [ "$LAST"  -gt "0"     ] && { ELecho; BLecho;}
    fi
done

谢谢Thom,我喜欢你在这里所做的事情。不过这个函数是从运行脚本的目录开始的,我该如何将它指向变量$ROOT,其中我已经设置了起始目录?我意识到变量名可能有点误导性。 - Longman
谢谢Thom,我们正在正确的轨道上。你的函数已经正确地扫描了文件夹/文件,但是它仍然没有保持层次结构。我在我的问题中添加了一个示例输出,以展示输出应该是什么样子的。 - Longman
啊,是的,谢谢你提供输出的例子。现在我明白你想要实现什么了。我之前是用另一种方式做的,需要重新修改一下。请耐心等待,这需要一些时间。 - thom
我非常感激你的帮助。我也会继续尝试这个。 - Longman

0

我使用这个脚本来生成一个HTML目录:

#!/bin/sh

# find ./ | grep -i "\.*$" >files
find ./ | sed -E -e 's/([^ ]+[ ]+){8}//' | grep -i "\.*$">files
listing="files"

out=""

html="index.html"
out="basename $out.html"
html="index.html"
cmd() {

  echo '  <!DOCTYPE html>'
  echo '<html>'
  echo '<head>'

  echo '  <meta http-equiv="Content-Type" content="text/html">'

  echo '  <meta name="Author" content="Bryan Guner">'
  echo '<link rel="stylesheet" href="./assets/prism.css">'
  echo ' <link rel="stylesheet" href="./assets/style.css">'
  echo ' <script async defer src="./assets/prism.js"></script>'

  echo "  <title> directory </title>"

  echo ""
  echo '<style>'


echo '    a {'
echo '      color: black;'
echo '    }'
echo ''
echo '    li {'
echo '      border: 1px solid black !important;'
echo '      font-size: 20px;'
echo '      letter-spacing: 0px;'
echo '      font-weight: 700;'
echo '      line-height: 16px;'
echo '      text-decoration: none !important;'
echo '      text-transform: uppercase;'
echo '      background: #194ccdaf !important;'
echo '      color: black !important;'
echo '      border: none;'
echo '      cursor: pointer;'
echo '      justify-content: center;'
echo '      padding: 30px 60px;'
echo '      height: 48px;'
echo '      text-align: center;'
echo '      white-space: normal;'
echo '      border-radius: 10px;'
echo '      min-width: 45em;'
echo '      padding: 1.2em 1em 0;'
echo '      box-shadow: 0 0 5px;'
echo '      margin: 1em;'
echo '      display: grid;'
echo '      -webkit-border-radius: 10px;'
echo '      -moz-border-radius: 10px;'
echo '      -ms-border-radius: 10px;'
echo '      -o-border-radius: 10px;'
echo '    }'
echo '  </style>'
  echo '</head>'

  echo '<body>'

  echo ""

  # continue with the HTML stuff

  echo ""

  echo ""

  echo "<ul>"

  awk '{print "<li><a href=\""$1"\">",$1,"&nbsp;</a></li>"}' $listing

  # awk '{print "<li>"};

  #     {print " <a href=\""$1"\">",$1,"</a></li>&nbsp;"}' \ $listing

  echo ""

  echo "</ul>"

  echo "</body>"

  echo "</html>"

}

cmd $listing --sort=extension >>$html



你可以使用以下内容来删除指向不需要的扩展名类型的文件的链接:

sudo sed -i '/\.git/d' ./index.html
sudo sed -i '/node_modules/d' ./index.html
sudo sed -i '/right\.html/d' ./index.html
sudo sed -i '/sol\~/d' ./right.html
sudo sed -i '/\.json/d' ./index.html
sudo sed -i '/\.gif/d' ./index.html
sudo sed -i '/\.png/d' ./index.html
sudo sed -i '/\.css/d' ./index.html
sudo sed -i '/\.js/d' ./index.html
sudo sed -i '/\.php/d' ./index.html
sudo sed -i '/\.md/d' ./index.html
sudo sed -i '/\.svg/d' ./index.html
sudo sed -i '/\.jpeg/d' ./index.html
sudo sed -i '/\.jpg/d' ./index.html
sudo sed -i '/\.scss/d' ./index.html
sudo sed -i '/scss/d' ./index.html
sudo sed -i '/\.txt/d' ./index.html
sudo sed -i '/\.ttf/d' ./index.html
sudo sed -i '/\.pdf/d' ./index.html
sudo sed -i '/\.mp4/d' ./index.html
sudo sed -i '/\.pug/d' ./index.html
sudo sed -i '/\.DS_store/d' ./index.html
sudo sed -i '/\.DS_Store/d' ./index.html
sudo sed -i '/\.sql/d' ./index.html
sudo sed -i '/\.py/d' ./index.html
sudo sed -i '/\.xlsx/d' ./index.html
sudo sed -i '/\.JPG/d' ./index.html
sudo sed -i '/\.TTF/d' ./index.html
sudo sed -i '/\.ttf/d' ./index.html
sudo sed -i '/\.eot/d' ./index.html
sudo sed -i '/\.ttc/d' ./index.html
sudo sed -i '/\.PNG/d' ./index.html
sudo sed -i '/\.java/d' ./index.html
sudo sed -i '/\.png/d' ./index.html
sudo sed -i '/\.jpeg/d' ./index.html
sudo sed -i '/\.gif/d' ./index.html
sudo sed -i '/\.go/d' ./index.html
sudo sed -i '/\.vue/d' ./index.html

结果就像一个目录页面:

网站


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接