Introduction to Bash Scripts

We will talk about Bash programming language. The Bash script is written in a text editor. We can use mousepad.

It is very important that bash script must start with #!/bin/bash.

And remember to save as xxx.sh

#!/bin/bash

# This is the first program

echo 'hello world'

And remember to give execute permission

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./ 1.sh
zsh: permission denied: ./

┌──(root㉿kali)-[~/Desktop/lab3]
└─# chmod +x 1.sh

Then, it can be executed

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./1.sh 
hello world

For loop

Then, the first program is;

#!/bin/bash

# This program prints text to the screen
# Prompt the user to enter some  text

echo "Enter text message: "
read mymessage 

printf "Enter the number of times to print: "
read timestoprint

for ((i=0; i<$timestoprint; i++))
do
   printf "You typed: $mymessage, it will be printed $timestoprint times! \n"
done
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program.sh 
Enter text message: 
nihao
Enter the number of times to print: 3
You typed: nihao, it will be printed 3 times! 
You typed: nihao, it will be printed 3 times! 
You typed: nihao, it will be printed 3 times! 

We can add -n after echo that keep the cursor one the same line.
Also, we can use %s and %d to format the string and integer inputs.

#!/bin/bash

# 这个程序会在屏幕上打印文本
# 提示用户输入一些文本

echo -n "Enter text message: " # 提示用户输入消息,-n 选项让光标停留在同一行上,不换行
read mymessage # 读取用户输入的文本并存储在变量 mymessage 中

printf "Enter the number of times to print: " # 提示用户输入打印次数
read timestoprint # 读取用户输入的打印次数并存储在变量 timestoprint 中

for ((i=0; i<$timestoprint; i++)) # 使用for循环,循环次数为用户指定的次数
do
   printf "You typed: %s, it will be printed %d times! \n" "$mymessage" "$timestoprint"
   # 使用printf格式化输出,将用户输入的文本和打印次数显示出来
done

The all below should be work fine, but the second one is much better syntax.
%s means string format

printf $mymessage
or printf “%s” mymessage
or printf “%s $mymessage”

The for loop could be different style,

  • The first one is iterate over a list
for variable in value1 value2 value3
do
    # Commands to execute
done
#!/bin/bash

for fruit in apple banana cherry
do
    echo "Fruit: $fruit"
done

The result will be:

Fruit: apple
Fruit: banana
Fruit: cherry
  • The second one should be iterating over commands
for variable in $(command)
do
    # Commands to execute
done

For exmaple,

#!/bin/bash

for file in $(ls)
do
    echo "File: $file"
done

The result will be

File: file1.txt
File: file2.txt
File: directory1
  • The last one is belike C language
for (( i=0; i<end_value; i++ ))
do
    # Commands to execute
done

For example:

#!/bin/bash

for (( i=1; i<=5; i++ ))
do
    echo "Number $i"
done

The result will be

Number 1
Number 2
Number 3
Number 4
Number 5

\n means start from a new line

While loop

#!/bin/bash

# This program asks user for a number
# It loops adding the number to the total until
# The total exceeds 100

total=0;
loopcount=0;

# Prompt the user to enter a number less than 100
printf "Enter a number less than 100: "
read numentered

# Detect if the number entered is between 1-99
while ((numentered < 1 || numentered > 99))
do
  printf "Enter a number less than 100: " 
  read numentered
done

# Calculate the number, and loop times
while ((total < ((100-numentered))))
do
   ((loopcount++))
   printf "loop $loopcount \n"
   ((total = total + numentered))
   printf "The number is $total now \n"
done

# Print the result of total number and loop times.
if ((loopcount > 1))
then
   printf "There were $loopcount loops, and the total is $total. \n"
else
   printf "There was one loop, and the total is $total. \n"
fi

The result is

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program2.sh
Enter a number less than 100: a
Enter a number less than 100: -1
Enter a number less than 100: 101
Enter a number less than 100: 33
loop 1 
The number is 33 now 
loop 2 
The number is 66 now 
loop 3 
The number is 99 now 
There were 3 loops, and the total is 99. 

But if I type a non-integer and between 1-100, this program will be error because ((...)) snytax only can process integer calculate.

So, we need value if the type in number is integer by regular expression

"$numentered" =~ ^[0-9]+$

"$numentered":这是一个变量,包含用户输入或其他字符串值。用引号包围可以确保变量的值被正确处理,即使它包含空格或特殊字符。

=~:这是 Bash 中用于进行正则表达式匹配的操作符。它用来测试左边的字符串是否与右边的正则表达式模式匹配。

^[0-9]+$:这是一个正则表达式模式。它的含义如下:

^: 匹配输入的开始位置。

[0-9]+: 匹配一个或多个数字。[0-9] 表示数字字符,+ 表示一个或多个的数量。

$: 匹配输入的结束位置。

结合起来,这个模式匹配的字符串必须从开始到结束都是由一个或多个数字组成的,即正整数

After updating,

#!/bin/bash

# This program asks user for a number
# It loops adding the number to the total until
# The total exceeds 100

total=0
loopcount=0

# Prompt the user to enter a number less than 100
while true; do
    printf "Enter a number between 1 and 99: "
    read numentered

    # Validate input
    if [[ "$numentered" =~ ^[0-9]+$ ]] && ((numentered >= 1 && numentered <= 99)); then
        break
    else
        printf "Invalid input. Please enter a valid integer between 1 and 99.\n"
    fi
done

# Calculate the total and loop count
while ((total + numentered <= 100)); do
    ((loopcount++))
    printf "Loop $loopcount\n"
    ((total += numentered))
    printf "The total is now $total\n"
done

# Print the result of total number and loop times
if ((loopcount > 1)); then
    printf "There were $loopcount loops, and the total is $total.\n"
else
    printf "There was one loop, and the total is $total.\n"
fi

The result:

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program2GPT.sh       
Enter a number between 1 and 99: 1.3
Invalid input. Please enter a valid integer between 1 and 99.
Enter a number between 1 and 99: A
Invalid input. Please enter a valid integer between 1 and 99.
Enter a number between 1 and 99: 101
Invalid input. Please enter a valid integer between 1 and 99.
Enter a number between 1 and 99: 45
Loop 1
The total is now 45
Loop 2
The total is now 90
There were 2 loops, and the total is 90.

In Bash scripting, the while loop allows you to repeatedly execute a block of code as long as a specified condition is true.

while [CONDITION]
do
    # Commands to be executed
done

Example is

#!/bin/bash

count=1

while [ $count -le 5 ]
do
    echo "Number: $count"
    ((count++))  # Increment count by 1
done

-le less than or equal

Infinite Loop

#!/bin/bash

while true
do
    echo "This loop runs forever. Press Ctrl+C to stop."
    sleep 1  # Pause for 1 second between iterations
done

If statement

In Bash scripting, the if statement is used for conditional execution of commands based on whether a specified condition is true or false.

if [CONDITION]; then
    # Commands to be executed if CONDITION is true
elif [ANOTHER_CONDITION]; then
    # Commands to be executed if ANOTHER_CONDITION is true
else
    # Commands to be executed if no previous conditions are true
fi

example

#!/bin/bash

number=15

if [ $number -gt 10 ]; then
    echo "The number is greater than 10."
fi
#!/bin/bash

file="example.txt"

if [ -e "$file" ]; then
    echo "The file exists."
else
    echo "The file does not exist."
fi
#!/bin/bash

number=25

if [ $number -lt 10 ]; then
    echo "The number is less than 10."
elif [ $number -lt 20 ]; then
    echo "The number is between 10 and 19."
else
    echo "The number is 20 or greater."
fi
Key Points
Comparison Operators:
-eq : Equal  
-ne : Not equal  
-lt : Less than  
-le : Less than or equal to  
-gt : Greater than  
-ge : Greater than or equal to 

String Operators:  
= : Equal  
!= : Not equal  
< : Less than (lexicographically)  
> : Greater than (lexicographically)  

File Test Operators:  
-e : Exists  
-f : Regular file  
-d : Directory  
-r : Readable  
-w : Writable  
-x : Executable  

random number

The command printf $(($RANDOM % 10 + 1)) generates a random number between 1 and 10 and prints it.

$RANDOM:
RANDOM is a built-in Bash variable that generates a random integer between 0 and 32767 each time it is accessed.

$(($RANDOM % 10 + 1)):
This is an arithmetic expression inside $(( ... )), which evaluates mathematical expressions in Bash.

Expression Breakdown:
$RANDOM % 10:
% is the modulus operator, which calculates the remainder of dividing $RANDOM by 10. This effectively limits the result to a range from 0 to 9.

$RANDOM % 10 + 1:
Adding 1 shifts the range from 0-9 to 1-10.
#!/bin/bash

# This program will generate a number between one and ten
# The user tries to guess the number

printf "Guess what random number between 1 and 10 is generated:  \n"
read guess

randomnumber=$(($RANDOM % 10 + 1))

if (($guess == randomnumber))
then
   printf "Congratulations! You are correct \n"
else
   printf "Sorry, you are wrong! The number is $randomnumber \n"
fi

However, same error like below

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program3.sh
Guess what random number between 1 and 10 is generated:  
1.2
./program3.sh: line 12: ((: 1.2 == randomnumber: syntax error: invalid arithmetic operator (error token is ".2 == randomnumber")
Sorry, you are wrong! The number is 6 

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program3.sh
Guess what random number between 1 and 10 is generated:  
A
Sorry, you are wrong! The number is 5 

So, we can update it

#!/bin/bash

# This program generates a random number between 1 and 10
# The user tries to guess the number

# Function to validate input
is_valid_guess() {
    [[ $1 =~ ^[0-9]+$ ]] && [ $1 -ge 1 ] && [ $1 -le 10 ]
}

# Generate a random number between 1 and 10
randomnumber=$(($RANDOM % 10 + 1))

# Prompt the user to guess the number
while true; do
    printf "Guess the random number between 1 and 10: "
    read guess

    if is_valid_guess "$guess"; then
        break
    else
        printf "Invalid input. Please enter a number between 1 and 10.\n"
    fi
done

# Check if the user's guess is correct
if [ "$guess" -eq "$randomnumber" ]; then
    printf "Congratulations! You are correct.\n"
else
    printf "Sorry, you are wrong! The number was $randomnumber.\n"
fi

$1: The first argument passed to a script or function.

The result will be better:

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program3GPT.sh       
Guess the random number between 1 and 10: a
Invalid input. Please enter a number between 1 and 10.
Guess the random number between 1 and 10: 1.2
Invalid input. Please enter a number between 1 and 10.
Guess the random number between 1 and 10: -1
Invalid input. Please enter a number between 1 and 10.
Guess the random number between 1 and 10: 3
Sorry, you are wrong! The number was 8.

We can combine while loop and random function

#!/bin/bash

# This program will generate a number between one and ten
# The user tries to guess the number

printf "Guess what random number between 1 and 10 is generated:  \n"
read guess

randomnumber=$(($RANDOM % 10 + 1))
numberguesses=1

while (($guess != $randomnumber))
do
   printf "Sorry, you are wrong! Try again! \n"
   read guess
   ((numberguesses++))
done

if (($numberguesses ==1))
then 
   printf "Congratulations! You are correct and took one time! \n"
else
   printf "Congratulations! You are correct and took $numberguesses times! \n"
fi
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program4.sh   
Guess what random number between 1 and 10 is generated:  
2
Sorry, you are wrong! Try again! 
3
Congratulations! You are correct and took 2 times! 

And to avoid the input value is not integer, we can modify this program as

#!/bin/bash

# 这个程序生成一个 1 到 10 之间的随机数字
# 用户尝试猜测这个数字

# 生成一个 1 到 10 之间的随机数字
randomnumber=$(($RANDOM % 10 + 1))

# 初始化猜测次数
numberguesses=0

# 函数:检查输入是否为有效的整数
is_valid_number() {
    [[ $1 =~ ^[0-9]+$ ]] && [ $1 -ge 1 ] && [ $1 -le 10 ]
}

# 提示用户猜测
while true; do
    printf "Guess a number between 1 and 10: "
    read guess

    if is_valid_number "$guess"; then
        break
    else
        printf "Invalid input. Please enter an integer between 1 and 10.\n"
    fi
done

# 记录第一次猜测
numberguesses=1

# 循环直到用户猜对为止
while (($guess != $randomnumber))
do
   printf "Sorry, you are wrong! Try again! \n"

   # 读取用户的新猜测
   while true; do
       read guess

       if is_valid_number "$guess"; then
           break
       else
           printf "Invalid input. Please enter an integer between 1 and 10.\n"
       fi
   done

   ((numberguesses++))
done

# 猜测正确后的反馈
if (($numberguesses == 1))
then 
   printf "Congratulations! You are correct and took one attempt! \n"
else
   printf "Congratulations! You are correct and took $numberguesses attempts! \n"
fi
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program4GPT.sh       
Guess a number between 1 and 10: 2
Sorry, you are wrong! Try again! 
3
Sorry, you are wrong! Try again! 
4
Sorry, you are wrong! Try again! 
6
Sorry, you are wrong! Try again! 
1
Sorry, you are wrong! Try again! 
5
Sorry, you are wrong! Try again! 
10
Sorry, you are wrong! Try again! 
9
Congratulations! You are correct and took 8 attempts! 

Working with Hex

$((16#$thehex))

16#:This specifies that the number following it is in base 16 (hexadecimal). The # symbol is used to indicate that a number is in a different base.

$thehex:This is a variable that contains a hexadecimal number as a string. For example, if thehex is 1A, it represents the hexadecimal number 1A.

$((16#$thehex)):This performs arithmetic evaluation by converting the hexadecimal number contained in $thehex to its decimal (base 10) equivalent.

bc 是 Linux 和 Unix 系统中的任意精度计算器,它的语法允许你进行各种数学运算和进制转换。以下是 bc 的基本语法和常见用法:

基本语法
启动 bc:在终端输入 bc 启动 bc 交互式环境。也可以通过管道将表达式传递给 bc 执行,并立即返回结果。

例如:

echo "3 + 5" | bc

输入基数 (ibase):ibase 用于指定输入的数值的进制(默认为 10)。
例如,将输入基数设置为 16:

ibase=16

输出基数 (obase):obase 用于指定输出结果的进制(默认为 10)。
例如,将输出基数设置为 2:

obase=2

设置小数精度 (scale):scale 用于指定小数点后保留的位数,默认情况下是 0,即不保留小数。
例如,设置小数点后保留两位:

scale=2

常见用法示例

简单算术运算:
加法:

echo "3 + 5" | bc  # 输出: 8

乘法:

echo "4 * 7" | bc  # 输出: 28

除法并保留小数:

echo "scale=2; 10 / 3" | bc  # 输出: 3.33

进制转换:

十六进制转换为二进制:

echo "obase=2; ibase=16; 1A" | bc  # 输出: 11010

二进制转换为十进制:

echo "obase=10; ibase=2; 11010" | bc  # 输出: 26

平方根计算:

计算 25 的平方根:

echo "scale=4; sqrt(25)" | bc  # 输出: 5.0000

乘方运算:

计算 2 的 3 次方:

echo "2^3" | bc  # 输出: 8

条件判断:

判断 5 是否大于 3:

echo "5 > 3" | bc  # 输出: 1 (1 表示真,0 表示假)

退出 bc
在交互式模式下,输入 quit 或按 Ctrl+D 可以退出 bc。


# This program converts hex to decimal an binary

printf "Enter the hex to be converted to decimal and binary: "
read thehex

printf "Hexadecimal $thehex in decimal is %d \n" $((16#$thehex))
printf "Hexadecimal $thehex in binary is " 
echo "obase=2; ibase=16; ${thehex}" | bc 
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./hexdec.sh
Enter the hex to be converted to decimal and binary: 10
Hexadecimal 10 in decimal is 16 
Hexadecimal 10 in binary is 10000

Same issue is that this program cannot process non-integer value, so update it

^:匹配字符串的开始。确保匹配必须从字符串的开头开始。
[0-9A-Fa-f]:这是一个字符集,用来匹配一个十六进制字符。它包含以下范围:
0-9:匹配任意一个数字字符(0 到 9)。
A-F:匹配任意一个大写字母(A 到 F)。
a-f:匹配任意一个小写字母(a 到 f)。
+:匹配前面的字符集(即 [0-9A-Fa-f])一次或多次。这意味着该正则表达式至少要匹配一个字符,且可以匹配任意数量的字符,只要它们都是有效的十六进制字符。
$:匹配字符串的结尾。确保整个字符串完全由有效的十六进制字符组成,没有其他字符。

#!/bin/bash

# 这个程序将用户输入的十六进制数转换为十进制和二进制

# 提示用户输入
printf "Enter the hex to be converted to decimal and binary: "
read thehex

# 验证输入是否为有效的十六进制数
if ! [[ $thehex =~ ^[0-9A-Fa-f]+$ ]]; then
  echo "Invalid hexadecimal number. Please enter a valid hex value."
  exit 1
fi

# 将十六进制转换为十进制并输出
printf "Hexadecimal $thehex in decimal is %d \n" $((16#$thehex))

# 将十六进制转换为二进制并输出
printf "Hexadecimal $thehex in binary is " 
echo "obase=2; ibase=16; ${thehex^^}" | bc

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./hexdec.sh
Enter the hex to be converted to decimal and binary: gg
./hexdec.sh: line 8: 16#gg: value too great for base (error token is "16#gg")
Hexadecimal gg in binary is 0

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./hexdecGPT.sh       
Enter the hex to be converted to decimal and binary: gg
Invalid hexadecimal number. Please enter a valid hex value.

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./hexdecGPT.sh  
Enter the hex to be converted to decimal and binary: ff
Hexadecimal ff in decimal is 255 
Hexadecimal ff in binary is 11111111

Searching Binary

Now, we utilise a command to search files through binary.

We then create a .dd image of the usb stick. The data area of the FAT Formatted USB stick
beginning at hex 0x9800 which in decimal is 38,913. If we divide this by 512, we find that the
first jpg begins at sector 76.

The jpg files are located at:
0x9800 = 38,912 which is sector 76
0xB000 = 45,056 which is sector 88
0xE800 = 59,392 which is sector 116
0x10000 = 65,536 which is sector 128
0x11000 = 69,632 which is sector 136

This final jpg ends with 0x192EC which is 103,148 in decimal. This is part way through sector

  1. Sector 202 begins at 103,424 and there are no files after this, so I can create an image of
    the usb stick that is 103,424 bytes and this will include the 5 jpg files. I shall check that all files are easily recovered with Foremost.
#!/bin/bash

# This script searches a .dd file and gives thenumber of jpg files

#printf "Enter the .dd filename including path: "
#read filename

filename="/root/Desktop/myfile.dd"

#numjpgfiles="$(LC_ALL=C grep -cP '\xFF\xD8\xFF\xE0' $filename)"

jpglist="$(LC_ALL=C grep -obUaPe '\xFF\xD8\xFF\xE0' $filename | awk -F: '{print $1}')"
arrayjpgstart=($jpglist)
numjpgfiles=${#arrayjpgstart[*]}

printf "There are %d jpg files in image $filename \n" $numjpgfiles
printf "The jpg files begin at byte: \n"

for ((i=0;i<numjpgfiles;i++))
do
  start=${arrayjpgstart[i]}
  start=$((start))
  sectorcount=$((start / 512))
  printf "Start = %d \t whin in hex = 0x%x in sector %d \n" $start $start $sectorcount 
done
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./searchhex.sh 
There are 5 jpg files in image /root/Desktop/lab3/myfile.dd 
The jpg files begin at byte: 
Start = 38912    whin in hex = 0x9800 in sector 76 
Start = 45056    whin in hex = 0xb000 in sector 88 
Start = 59392    whin in hex = 0xe800 in sector 116 
Start = 65536    whin in hex = 0x10000 in sector 128 
Start = 69632    whin in hex = 0x11000 in sector 136 

LC_ALL=C grep -obUaPe '\xFF\xD8\xFF\xE0' 是一个用于在文件中查找特定二进制模式的命令。让我们逐部分解析这个命令。

命令解释

LC_ALL=C:这是一个环境变量设置,作用是在执行命令时指定使用 C 或 POSIX 本地化(locale)。在 C 本地化中,字符集是标准的 ASCII,没有语言特定的排序和字符比较规则。
使用 LC_ALL=C 可以确保 grep 在处理二进制文件时,不会受到本地化设置的影响,特别是在涉及非ASCII字符时。

grep:grep 是一个搜索文本的命令行工具,用于在文件或输入中查找匹配指定模式的行。

选项说明:
-o:只输出匹配的部分,而不是整行。在这个命令中,它会只输出匹配的字节序列。

-b:输出每个匹配项在文件中的字节偏移量(即该匹配项在文件中的位置,以字节为单位)。这个选项很有用,当你需要知道在文件中某个模式出现的位置时。

-U:以二进制模式处理文件。默认情况下,grep 会认为文件是文本文件,使用 -U 可以告诉 grep 把文件当作二进制文件来处理,不会因为遇到二进制数据而终止处理。

-a:将文件作为文本处理,即使文件包含二进制数据。这个选项可以与 -U 一起使用,确保在处理可能包含二进制数据的文件时,grep 不会忽略这些数据。

-P:使用 Perl 兼容的正则表达式(Perl-compatible regular expressions,简称 PCRE)。这比基本的正则表达式功能更强大,允许使用更复杂的模式。

'\xFF\xD8\xFF\xE0':这是一个表示 JPEG 文件头的二进制模式(也称为魔术字节)。grep 将在文件中查找这个字节序列。

\xFF, \xD8, \xFF, \xE0 是十六进制表示的字节,每个字节用 \x 前缀指定。例如,\xFF 代表 255,而 \xD8 代表 216。

awk:awk 是一个强大的文本处理工具,通常用于从文件或输入中提取和处理数据。它以逐行的方式工作,并可以基于字段或列来操作文本。

-F::-F 选项用来指定字段分隔符。在这个例子中,字段分隔符被设置为 :(冒号)。这意味着 awk 将每一行文本按照冒号进行拆分,将它们分成多个字段。

'{print $1}':{print $1} 是 awk 的操作部分,表示输出每一行的第一个字段(即 $1)。在 awk 中,$1 代表当前行的第一个字段,$2 代表第二个字段,以此类推。

Add one function to verify if the file is existed, and replace some command:


# Define the file to be checked
filename="/root/Desktop/myfile.dd"

# Check if the file exists
if [[ ! -f "$filename" ]]; then
    echo "Error: File $filename not found." 1>&2
    exit 1
fi

# Find the starting byte positions of JPEG headers
jpglist=($(LC_ALL=C grep -obUaP '\xFF\xD8\xFF\xE0' "$filename" | cut -d: -f1))
numjpgfiles=${#jpglist[@]}

# Print the number of JPEG files found
printf "There are %d jpg files in image %s \n" "$numjpgfiles" "$filename"
printf "The jpg files begin at byte: \n"

# Loop through the start positions and print details
for start in "${jpglist[@]}"; do
  sectorcount=$((start / 512))
  printf "Start = %d \t which in hex = 0x%x in sector %d \n" "$start" "$start" "$sectorcount"
done
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./searchhexGPT.sh
Error: File /root/Desktop/myfile.dd not found.

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./searchhexGPT.sh       
There are 5 jpg files in image /root/Desktop/lab3/myfile.dd 
The jpg files begin at byte: 
Start = 38912    which in hex = 0x9800 in sector 76 
Start = 45056    which in hex = 0xb000 in sector 88 
Start = 59392    which in hex = 0xe800 in sector 116 
Start = 65536    which in hex = 0x10000 in sector 128 
Start = 69632    which in hex = 0x11000 in sector 136 

Arrays

Like other language, Bash also have Array function,

list_of_nums=(1 2 3 4 5 6 7 8 nine)
echo ${list_of_nums[*]}
arraylength=${#list_of_nums[*]}
printf "The array has $arraylength elements\n"

firstnum=${list_of_nums[3]}
secondnum=${list_of_nums[7]}
answer=$((firstnum*secondnum))
echo $answer
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./arrays.sh             
1 2 3 4 5 6 7 8 nine
The array has 9 elements
32
数组中包含字符串:由于数组中包含了一个字符串 "nine",而不是整数。尝试将 firstnum 和 secondnum 作为整数进行算术运算会导致错误,因为 nine 不能转换为数字。

正确的数组长度获取:获取数组长度的正确方式是 ${#list_of_nums[@]},而不是 ${#list_of_nums[*]}。这两者在大多数情况下是等效的,但使用 ${#list_of_nums[@]} 是更标准的做法。

错误处理:可以加入一些错误处理逻辑,确保数组索引有效且元素可以参与算术运算。
#!/bin/bash

list_of_nums=(1 2 3 4 5 6 7 8 nine)
echo "Array elements: ${list_of_nums[*]}"
arraylength=${#list_of_nums[@]}
printf "The array has %d elements\n" "$arraylength"

# Ensure indices are valid and elements are integers
if [[ ${list_of_nums[3]} =~ ^[0-9]+$ ]] && [[ ${list_of_nums[7]} =~ ^[0-9]+$ ]]; then
    firstnum=${list_of_nums[3]}
    secondnum=${list_of_nums[7]}
    answer=$((firstnum * secondnum))
    echo "The product of elements at index 3 and 7 is $answer"
else
    echo "Error: Non-integer values found at the specified indices."
fi

Additional Bash Scripting for Advanced Forensics LAB4

Now, let us combine these to check if a file is existing or not.

The entire script will be terminal and quit if set -e is triggered.
false means manually returning a false condition to invoke set -e
[ or [ ] or ] equal function: test, and -f "file name" is a parameter, so space is required.

read:这是一个 Bash 内置命令,用于从标准输入(通常是键盘)读取一行文本并将其存储在变量中。

-p 选项允许你在 read 命令前指定一个提示字符串。
"Please enter a line of text" 是提示用户输入的文本。这个提示会在用户输入之前显示给用户。

#!/bin/bash

# Thie script asks the user for a filename and then searches this file

read -p "Please enter the path and filename: " filename

if [ -f "$filename" ]
then
    printf "This file exists"
else
    printf "This file does not exist"
    set -e
    false
fi

printf "\n Failed to exit"

Different results, the script will quit if the file is not existing, and the last sentence will not be printed.

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program5.sh
Please enter the path and filename: myfile.dd
This file exists
Failed to exit                                             
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program5.sh
Please enter the path and filename: 1.dd
This file does not exist 

Upgrade vertion put set -e to the top,

#!/bin/bash

# 这个脚本要求用户输入文件名并搜索该文件。
# 如果文件不存在,set -e 会使整个脚本终止。
# false 用于手动返回一个失败状态以触发 set -e。
# [ 或 [ ] 的功能等同于 test,-f "filename" 是其参数,因此需要空格。

set -e  # 只要任何命令失败,整个脚本将立即退出

read -p "Please enter the path and filename: " filename

if [ -f "$filename" ]; then
    printf "This file exists\n"
else
    printf "This file does not exist\n"
    false  # 手动触发错误状态,导致脚本退出
fi

# 由于 set -e 的存在,如果上面的 false 被触发,脚本将在此处退出
printf "This line will only print if the file exists.\n"

Now, we can create a folder to save the resulting files from the script.

We need know the functionality of parameter extension

${variable#pattern}

Different patterns leads to different results.

${variable#pattern}

# 表示从变量值的开头开始,删除与 pattern 匹配的最短部分(即,尽可能少的字符)。
示例:对于 filename="example.file.txt",filename#*. 会删除第一个 . 之前的所有内容,结果是 file.txt

${variable##pattern}

## 表示从变量值的开头开始,删除与 pattern 匹配的最长部分(即,尽可能多的字符)。
示例:对于 filename="example.file.txt",filename##*. 会删除最后一个 . 之前的所有内容,结果是 txt

${variable%pattern}

% 表示从变量值的末尾开始,删除与 pattern 匹配的最短部分(即,尽可能少的字符)。
示例:对于 filename="example.file.txt",filename%.* 会删除最后一个 . 及其之后的所有内容,结果是 example.file

${variable%%pattern}

%% 表示从变量值的末尾开始,删除与 pattern 匹配的最长部分(即,尽可能多的字符)。
示例:对于 filename="example.file.txt",filename%%.* 会删除第一个 . 及其之后的所有内容,结果是 example

And some other common functions:

${variable/old/new}

用法:${filename/file/FILE}
解释:将变量 filename 中的第一个 file 替换为 FILE。

filename="file1 file2 file3"
echo "Replace first occurrence: ${filename/file/FILE}"
# 输出: Replace first occurrence: FILE1 file2 file3
${variable//old/new}

用法:${filename//file/FILE}
解释:将变量 filename 中的所有 file 替换为 FILE。

filename="file1 file2 file3"
echo "Replace all occurrences: ${filename//file/FILE}"
# 输出: Replace all occurrences: FILE1 FILE2 FILE3

We also can use mkdir to create folder

The –v switch is verbose mode which will confirm that a folder has been created

mkdir –v $filename

So, we can leverage these function to acquire info

#!/bin/bash

# Thie script asks the user for a filename and then searches this file
# The entire script will be terminal and quit if set -e is triggered.
# false means manually returning a false condition to invoke set -e 
# [ or [ ] or ] equal function: test, and -f "file name" is a parameter, so space is required.

read -p "Please enter the path and filename: " filename

if [ -f "$filename" ]
then
    printf "This file exists \n"
else
    printf "This file does not exist \n"
    set -e
    false
fi

# Create the folder for the files and name it the same as the input filename

# Trim everything before the .
filenameextension="${filename##*.}"

# Trim everything including the . and everything after this
filenameonly="${filename%.*}"

mkdir -v $filenameonly

printf "Filename = $filename \n"
printf "Fileextension $fileextension \n"
printf "Filenameonly = $filenameonly \n"

The results should be like below:

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program6.sh
Please enter the path and filename: myfile.dd
This file exists 
mkdir: cannot create directory ‘myfile’: File exists
Filename = myfile.dd 
Fileextension  
Filenameonly = myfile 

┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program6.sh
Please enter the path and filename: 1.dd
This file does not exist 

Upgrade versiton

#!/bin/bash

# Thie script asks the user for a filename and then searches this file
# The entire script will terminate and quit if an error occurs.

# Enable strict mode: exit on error, undefined variables, and pipeline failures
set -euo pipefail

# Ask the user for a filename
read -p "Please enter the path and filename: " filename

# Check if the file exists
if [ -f "$filename" ]; then
    printf "This file exists\n"
else
    printf "This file does not exist\n"
    exit 1
fi

# Extract file extension and name without extension
filenameextension="${filename##*.}"
filenameonly="${filename%.*}"

# Create a directory with the same name as the file (without extension)
if [ ! -d "$filenameonly" ]; then
    mkdir -v "$filenameonly"
else
    printf "Directory '%s' already exists\n" "$filenameonly"
fi

# Display information about the file
printf "Filename = %s\n" "$filename"
printf "File extension = %s\n" "$filenameextension"
printf "Filename without extension = %s\n" "$filenameonly"

主要改进点
启用 set -euo pipefail:

-e 确保遇到错误时脚本终止。
-u 确保使用未定义的变量时脚本终止。
-o pipefail 确保管道中的任何命令失败时脚本终止。
检查目录是否存在:

在创建目录之前检查目录是否已经存在,以避免重复创建并提供反馈。
使用双引号:

使用双引号包裹变量,处理文件名或目录名中的空格和特殊字符。
退出脚本:

当文件不存在时,使用 exit 1 来退出脚本并返回错误代码。

Additional Bash Scripting for Advanced Forensics LAB 5

#!/bin/bash

read -p "Please enter a line of text:" textinput
textlength=$(echo "$textinput" | awk '{print length}')
printf "You entered string of %d characters\n" "$textlength"
echo $textinput:将 textinput 变量的值传递给 echo 命令。echo 会输出变量中的文本。  
|:管道符,将 echo 命令的输出传递给 awk 命令。
awk '{print length}':awk 是一个文本处理工具,{print length} 是一个 awk 脚本,用于计算输入文本的长度。  
length 是 awk 内置函数,返回当前行的字符数。
`(反引号):用于执行命令并将其输出结果赋值给变量 textlength。也可以使用 $() 语法来执行命令,如 $(echo $textinput | awk '{print length}')。
┌──(root㉿kali)-[~/Desktop/lab3]
└─# ./program7.sh
Please enter the path and filename: myfile.dd
This file exists 
mkdir: cannot create directory ‘myfile’: File exists
Filename = myfile.dd 
Fileextension  
Filenameonly = myfile 
Please enter a line of text:hellowor
You entered string of 8 characters

Then we can save the text entered to a file called 'mytext.txt' in the folder we created

echo $textinput > $filenameonly/"mytext.txt"
echo $textinput:将变量 textinput 的内容输出到标准输出(通常是终端)。这部分会将用户输入的文本输出。

>:这是一个输出重定向符号,将 echo 命令的输出重定向到指定的文件中,而不是显示在终端上。如果指定的文件已经存在,它会被覆盖;如果文件不存在,它会被创建。

$filenameonly/"mytext.txt":

$filenameonly 是一个变量,表示目录路径。
"mytext.txt" 是文件名。
"$filenameonly/mytext.txt" 是完整的文件路径,表示将用户输入的文本写入到这个路径的文件中。

And then we can convert this text to hex and append it to the file.

echo $textinput:将变量 textinput 的内容输出到标准输出(通常是终端)。这里假设 textinput 包含用户输入的文本。

xxd -ps -c 200
xxd:这是一个用于创建和查看十六进制转储的命令。
-ps:-p 或 -ps 选项表示将输入文本转换为纯十六进制表示,且不添加任何格式。
-c 200:-c 选项指定每行显示的字节数。这里 -c 200 表示每行最多显示 200 个字节的十六进制值。如果文本长度超过 200 字节,xxd 将继续到下一行。你可以根据需要调整这个值。

< <(...):这是一个 Bash 进程替代语法,用于将命令的输出作为输入提供给另一个命令。
在这种情况下,< <(...) 将 echo $textinput | xxd -ps -c 200 的输出提供给 read 命令。

read hexinput:从标准输入读取一行内容并将其存储在变量 hexinput 中。在这个上下文中,它将读取 xxd 命令生成的十六进制输出。

read hexinput < <(echo $textinput | xxd -ps -c 200)
echo $textinput > $filenameonly/"mytext.txt"
echo $hexinput >> $filenameonly/"mytext.txt"

Also, we hope to create a 'for' loop to work through the string and print the characters to a text file one at a time.

read eachletterp:read 命令从标准输入读取一行,并将其赋值给变量 eachletterp。

< <(...): 这是一个过程替换(process substitution)。<(...) 会将括号中的命令输出作为一个临时文件的内容提供给 read 命令。这意味着 read 从这个临时文件读取内容而不是从标准输入。

echo $textinput | dd bs=1 skip=$a count=2 status=none 2>/dev/null:
echo $textinput:将 textinput 变量的内容传递给 dd 命令。
dd 是一个用于转换和拷贝文件的命令。这里它用来从输入文本中提取特定部分。
bs=1:指定块大小为 1 字节,即每次读入一个字节。
skip=$a:跳过前 $a 字节。
count=2:读取接下来的 2 字节。
status=none:不显示 dd 命令的进度信息。
2>/dev/null:将标准错误输出重定向到 /dev/null,这样 dd 命令的错误信息不会显示在终端。

a=0
excl="!"
finaltext=""

for ((i=0; i<=($textlength/2);i++))
do
    read eachletterp < <(echo $textinput | dd bs=1 skip=$a count=2 status=none 2>/dev/null)
    finaltext="$finaltext$eachletterp$excl"
    a=$(($a+2))
done

echo $finaltext >> $filenameonly/"mytext.txt"
在 Unix 和类 Unix 系统中,文件描述符(file descriptors)用于标识进程的输入和输出流。标准文件描述符有三个主要的编号:

标准输入(stdin) - 文件描述符 0
标准输出(stdout) - 文件描述符 1
标准错误(stderr) - 文件描述符 2
这些文件描述符用来处理程序的输入和输出流。具体到 2>/dev/null 的使用:

解释 2>/dev/null
2>:指定要重定向的文件描述符。在这个例子中,2 代表标准错误(stderr)。
/dev/null:这是一个特殊的设备文件,它丢弃所有写入它的数据。任何写入 /dev/null 的数据都会被丢弃,相当于“黑洞”。
因此,2>/dev/null 的作用是将标准错误流中的输出重定向到 /dev/null,即丢弃错误信息。

为什么使用 2?
标准错误(stderr) 通常用于输出错误消息或诊断信息。如果不希望错误信息显示在终端上,或者如果错误信息不重要,可以将其重定向到 /dev/null。
< <(...) 这种语法是 Bash 中的一种特殊结构,称为进程替换(Process Substitution)。让我们详细解析一下为什么有两个 < 符号。

进程替换简介
<(...) 和 >(...) 是 Bash 中用于进程替换的语法。
进程替换允许你将一个命令的输出作为一个文件来使用,而这个文件是由 Bash 在内部生成的。
为什么是两个 <?
1. (...) 部分:进程替换
(...):将括号内的命令放入子进程中执行,命令的输出将被重定向为一个文件描述符。这部分相当于生成了一个“文件”,但实际上是一个命令的输出。
2. < <(...):输入重定向
<:单个 < 用于输入重定向。它从文件中读取数据。例如,command < file 表示将 file 的内容作为 command 的输入。

<(...):这是进程替换的具体语法,< 用于输入重定向,而 (...) 内的命令输出将作为这个输入的来源。

组合起来
< <(...):第一个 < 是输入重定向,第二个 <(...) 是进程替换生成的一个“文件”。实际上,< <(...) 表示从括号内命令的输出中读取数据。
read output < <(echo "Hello, World!")

echo "Hello, World!" 这个命令的输出被包裹在 (...) 中。
<(...) 表示将 echo 的输出作为一个文件。
read output 则从这个“文件”中读取数据。
为什么两个 <?
第一个 <:表示标准输入重定向,从一个文件或文件描述符中读取数据。
第二个 <(...):这是进程替换,意味着从一个命令的输出中读取数据。

So, the new version will write these to new file:

nihaoya
6e6968616f79610a
ni!ha!oy!a!

Also, we can upgrade this script:

#!/bin/bash

# This script asks the user for a filename, checks if it exists, 
# creates a directory named after the file (without extension), 
# and processes user input to store various outputs in that directory.

set -e  # Exit script on any command failure

# Prompt the user for a filename
read -p "Please enter the path and filename: " filename

# Check if the file exists
if [[ -f "$filename" ]]; then
    printf "This file exists \n"
else
    printf "This file does not exist \n"
    exit 1
fi

# Extract the file extension and the filename without the extension
filenameextension="${filename##*.}"
filenameonly="${filename%.*}"

# Create a directory named after the file (without the extension)
mkdir -vp "$filenameonly"

# Print extracted filename details
printf "Filename = %s \n" "$filename"
printf "File extension = %s \n" "$filenameextension"
printf "Filename only = %s \n" "$filenameonly"

# Prompt the user for a line of text
read -p "Please enter a line of text: " textinput

# Calculate the length of the entered text
textlength=${#textinput}
printf "You entered a string of %d characters \n" "$textlength"

# Convert the entered text to hex format
hexinput=$(echo -n "$textinput" | xxd -ps -c 200)

# Save the original and hex text to a file
outputfile="$filenameonly/mytext.txt"
{
    echo "$textinput"
    echo "$hexinput"
} > "$outputfile"

# Process the text: add an exclamation mark `!` after every two characters
a=0
excl="!"
finaltext=""

for ((i=0; i<textlength; i+=2)); do
    # Extract two characters from the text
    eachletterp="${textinput:$a:2}"
    finaltext+="$eachletterp$excl"
    a=$((a+2))
done

# Append the processed text to the output file
echo "$finaltext" >> "$outputfile"

printf "Processing complete. Results stored in %s \n" "$outputfile"
改进与优化的说明
set -e 放置位置:将 set -e 放在脚本的开头,以便在任何命令失败时立即退出整个脚本。

文件检查部分:

使用 [[ -f "$filename" ]] 替代 if [ -f "$filename" ],[[ ... ]] 更加灵活和安全。
提取文件名和扩展名:

直接使用变量替换语法提取文件名和扩展名,保留并展示它们,方便后续使用。
目录创建:

使用 mkdir -vp,这里的 -v 选项会详细输出创建的目录,-p 选项则会在需要时创建父目录,并避免重复错误。
文本长度计算:

使用 ${#textinput} 获取字符串长度,比通过 awk 更直接。
Hex 转换:

使用 echo -n 来避免末尾的换行符,从而确保十六进制转换结果正确。
字符串处理:

直接使用字符串操作 ${textinput:$a:2} 来提取每两个字符,而不是使用 dd,这样脚本会更简单且不容易出错。
输出文件处理:

使用大括号 { ... } 组合命令输出,可以更方便地将多行内容写入到同一个文件。
脚本末尾消息:

添加处理完成后的提示信息,告诉用户结果已存储在特定文件中。

Additional Bash Scripting for Advanced Forensics LAB 6

Add a case selection

PS3 是 Bash shell 中的一个特殊变量,用于自定义 select 命令的提示符。虽然它是一个预定义的变量,但你可以在脚本中给它赋值来改变 select 命令的提示语句。

解释:
PS3 变量:是 Bash 内置的一个环境变量,用于设置 select 命令的提示符。在 select 命令中,每次显示选项列表时,都会显示 PS3 变量的内容作为提示符,提示用户输入一个数字选择相应的选项。

PS3="Please choose an option: "

select 命令
select 命令用于创建一个简单的菜单,从中用户可以选择一个选项。它会自动生成一个数字编号的菜单,并等待用户输入编号来选择相应的选项。选择后,select 循环会将用户选择的选项赋值给一个变量。

#!/bin/bash

PS3="Please select a fruit: "

select fruit in Apple Banana Orange Quit
do
    echo "You selected: $fruit"

    if [ "$fruit" == "Quit" ]; then
        echo "Exiting..."
        break
    elif [ -n "$fruit" ]; then
        echo "You chose $fruit."
    else
        echo "Invalid selection."
    fi
done

当脚本运行时,select 命令会显示一个菜单:

1) Apple
2) Banana
3) Orange
4) Quit
Please select a fruit:

用户输入一个数字,例如 2,选择 Banana。

select 将选择的值赋给 fruit 变量,脚本会输出:

You selected: Banana
You chose Banana.

菜单会再次显示,直到用户选择 Quit。

选择 Quit 后,脚本会输出 "Exiting..." 并结束。

PS3 变量:自定义提示符为 "Please select a fruit: "。
select 循环:显示菜单并等待用户输入。
if 判断:根据用户输入决定是继续还是退出循环。
-n:在 elif 中用于检查 fruit 变量是否有内容,以判断用户选择是否有效。

[!NOTE]
在 select 命令中,默认情况下,选择一个选项后,脚本会继续循环,显示菜单并等待下一次选择。原因是 select 命令本身运行在一个循环内,所以当用户选择一个选项后,它会自动返回到菜单,并继续等待用户的输入。如果想要在选择后不继续循环,可以使用 break 语句来退出循环。

#!/bin/bash

PS3="Please select a fruit: "

select fruit in Apple Banana Orange Quit
do
    echo "You selected: $fruit"

    if [ "$fruit" == "Quit" ]; then
        echo "Exiting..."
        break  # 退出循环
    elif [ -n "$fruit" ]; then
        echo "You chose $fruit."
        break  # 在选项后退出循环
    else
        echo "Invalid selection."
    fi
done

case 语句是一个条件分支结构,用于根据变量的值执行不同的代码块。case 语句类似于其他编程语言中的 switch 语句,提供了一种简洁的方式处理多分支选择。

#!/bin/bash

# 提示用户输入一个选项
read -p "Please enter a number (1-3): " number

# 根据用户输入的值执行不同的代码块
case $number in
    1)
        echo "You selected option 1."
        ;;
    2)
        echo "You selected option 2."
        ;;
    3)
        echo "You selected option 3."
        ;;
    *)
        echo "Invalid option."
        ;;
esac
read -p:提示用户输入一个数字并将其存储在 number 变量中。
case 语句:检查 number 变量的值,并根据它执行相应的代码块。
1) 匹配输入为 1 的情况。
2) 匹配输入为 2 的情况。
3) 匹配输入为 3 的情况。
* 是默认情况,当输入不匹配任何已定义的选项时执行。
;;:结束每个匹配分支。

So, add these two to our script

# Create a menu to select
PS3="Enter a number: "

select character in ASCII HEX FINAL_OUTPUT ALL QUIT;
do
    echo "Selected character $character: "
    echo "Selected number: $REPLY"

    case $character in
        ASCII)
            echo $textinput > $filenameonly/"mytext.txt"
            ;;
        HEX)
            echo $hexinput > $filenameonly/"mytext.txt"
            ;;
        FINAL_OUTPUT)
            echo $finaltext > $filenameonly/"mytext.txt"
            ;;
        ALL)
            echo $textinput > $filenameonly/"mytext.txt"
            echo $hexinput >> $filenameonly/"mytext.txt"
            echo $finaltext >> $filenameonly/"mytext.txt"
            ;;
        QUIT)
            break
            ;;
        *)
            echo "Invalid option. Please select a valid number. "
            ;;
    esac
done

$REPLY是 select 命令的一个内置变量,专门用于存储用户在菜单中选择的选项编号。当你使用 select 命令创建菜单时,它会自动设置 $REPLY 变量为用户所选择的菜单项的编号。

工作原理
select 命令: 显示一个带有编号的菜单,并等待用户输入。
$REPLY: 自动保存用户输入的编号。

#!/bin/bash

# Thie script asks the user for a filename and then searches this file
# The entire script will be terminal and quit if set -e is triggered.
# false means manually returning a false condition to invoke set -e 
# [ or [ ] or ] equal function: test, and -f "file name" is a parameter, so space is required.

read -p "Please enter the path and filename: " filename

if [ -f "$filename" ]
then
    printf "This file exists \n"
else
    printf "This file does not exist \n"
    set -e
    false
fi

# Create the folder for the files and name it the same as the input filename

# Trim everything before the .
filenameextension="${filename##*.}"

# Trim everything including the . and everything after this
filenameonly="${filename%.*}"

# make a folder according to the input name
mkdir -v $filenameonly

# print all file information 
printf "Filename = $filename \n"
printf "Fileextension $fileextension \n"
printf "Filenameonly = $filenameonly \n"

# read a new type in content
read -p "Please enter a line of text:" textinput

# print the length of new content
textlength=$(echo $textinput | awk '{print length}')
printf "You entered string of $textlength characters \n" 

# convert the text to hex format
read hexinput < <(echo $textinput | xxd -ps -c 200)

# insert the text to new file in ACSII and Hex mode
echo $textinput > $filenameonly/"mytext.txt"
echo $hexinput >> $filenameonly/"mytext.txt"

a=0
excl="!"
finaltext=""

for ((i=0; i<=($textlength/2);i++))
do
    read eachletterp < <(echo $textinput | dd bs=1 skip=$a count=2 status=none 2>/dev/null)
    finaltext="$finaltext$eachletterp$excl"
    a=$(($a+2))
done

# Create a menu to select
PS3="Enter a number: "

select character in ASCII HEX FINAL_OUTPUT ALL QUIT;
do
    echo "Selected character $character: "
    echo "Selected number: $REPLY"

    case $character in
        ASCII)
            echo $textinput > $filenameonly/"mytext.txt"
            ;;
        HEX)
            echo $hexinput > $filenameonly/"mytext.txt"
            ;;
        FINAL_OUTPUT)
            echo $finaltext > $filenameonly/"mytext.txt"
            ;;
        ALL)
            echo $textinput > $filenameonly/"mytext.txt"
            echo $hexinput >> $filenameonly/"mytext.txt"
            echo $finaltext >> $filenameonly/"mytext.txt"
            ;;
        QUIT)
            break
            ;;
        *)
            echo "Invalid option. Please select a valid number. "
            ;;
    esac
done

优化一下

#!/bin/bash

# 这个脚本要求用户输入一个文件名,然后检查这个文件是否存在,
# 创建一个文件夹,并处理文本输入以生成各种输出。

# 提示用户输入文件名
read -p "请输入路径和文件名: " filename

# 检查文件是否存在
if [ -f "$filename" ]; then
    printf "文件存在。\n"
else
    printf "文件不存在。\n"
    set -e
    false  # 触发 set -e,退出脚本
fi

# 提取文件扩展名和去除扩展名的文件名
filenameextension="${filename##*.}"
filenameonly="${filename%.*}"

# 根据文件名创建目录
mkdir -v "$filenameonly"

# 显示文件信息
printf "文件名 = $filename\n"
printf "文件扩展名 = $filenameextension\n"
printf "去除扩展名的文件名 = $filenameonly\n"

# 从用户读取一行文本
read -p "请输入一行文本: " textinput

# 计算文本长度
textlength=${#textinput}
printf "你输入的字符串长度为 $textlength 个字符。\n"

# 将文本转换为十六进制格式并去除结尾的换行符
hexinput=$(echo -n "$textinput" | xxd -p -c 200 | tr -d '\n')

# 将文本输入和十六进制输出写入文件
echo "$textinput" > "$filenameonly/mytext.txt"
echo "$hexinput" >> "$filenameonly/mytext.txt"

# 将文本处理为最终格式并添加分隔符
a=0
excl="!"
finaltext=""

for ((i=0; i<textlength; i+=2)); do
    eachletterp=$(echo "$textinput" | dd bs=1 skip=$a count=2 status=none 2>/dev/null)
    finaltext="$finaltext$eachletterp$excl"
    a=$((a+2))
done

# 创建选择菜单
PS3="请输入数字: "

select character in ASCII HEX FINAL_OUTPUT ALL QUIT; do
    echo "选择的选项: $character"
    echo "选择的编号: $REPLY"

    case $character in
        ASCII)
            echo "$textinput" > "$filenameonly/mytext.txt"
            ;;
        HEX)
            echo "$hexinput" > "$filenameonly/mytext.txt"
            ;;
        FINAL_OUTPUT)
            echo "$finaltext" > "$filenameonly/mytext.txt"
            ;;
        ALL)
            echo "$textinput" > "$filenameonly/mytext.txt"
            echo "$hexinput" >> "$filenameonly/mytext.txt"
            echo "$finaltext" >> "$filenameonly/mytext.txt"
            ;;
        QUIT)
            break
            ;;
        *)
            echo "无效的选项。请选择一个有效的数字。"
            ;;
    esac
done

Chao

一个三天打鱼两天晒网的博主 拖延症严重患者 干啥啥不行,学啥啥不会