22、Bash脚本实用元素与脚本语言对比

Bash脚本实用元素与脚本语言对比

1. Bash中的算术运算

Bash主要处理文本,如命令、参数和文件名等。不过,它也能计算常见的算术表达式,只需将表达式用双括号 ((expression)) 括起来。由于许多算术字符(如 * ( ) )会被shell特殊解析,若要在脚本中将参数作为数学表达式处理,最好给参数加上引号。

例如,创建一个名为 arith 的脚本:

#!/bin/bash
answer=$(( $* ))
echo $answer

运行脚本:

admin@server1:~$ ./arith "(8+1)*(7-1)-60"
-6
admin@server1:~$ ./arith "2**60"
1152921504606846976

最新版本的Bash支持64位整数(范围是 -9223372036854775808 到 9223372036854775807),旧版本仅支持32位整数(范围是 -2147483648 到 2147483647),且不支持浮点数。若脚本需要处理浮点数或更高级的运算符,可使用外部程序,如 bc

在算术表达式中,使用变量时无需加 $ 符号:

#!/bin/bash
a=$1
b=$(( a+2 ))
echo "$a + 2 = $b"
c=$(( a*2 ))
echo "$a * 2 = $c"

运行脚本:

admin@server1:~$ ./arithexp 6
6 + 2 = 8
6 * 2 = 12
2. if语句

根据测试结果,可执行不同的代码块。Bash使用 if ... fi 语法,还可选择使用 elif (else if)和 else 部分:

if expression1 ; then
    (commands)
elif expression2 ; then
    (commands)
    ...
elif expressionN ; then
    (commands)
else
    (commands)
fi

行尾的 ; then 也可写成在下一行单独的 then

if expression
then
    (commands)
fi

示例:

admin@server1:~$ if [[ -x hello ]]
> then
> echo "hello is executable"
> fi
hello is executable

下面是一个更复杂的脚本,用于在 /etc/passwd 文件中查找账户名:

#!/bin/bash
USERID="$1"
DETECTED=$( egrep -o "^$USERID:" < /etc/passwd )
if [[ -n  "${DETECTED}" ]] ; then
    echo "$USERID is one of us    :-)"
else
    echo "$USERID is a stranger   :-("
fi

将此脚本命名为 friendorfoe 并使其可执行,然后测试:

admin@server1:~$ ./friendorfoe root
root is one of us   :-)
admin@server1:~$ ./friendorfoe sasquatch
sasquatch is a stranger  :-(
3. 简单脚本的故障排除

有一个用于删除文件或目录的脚本 delete 存在问题:

#!/bin/bash
if rm $1
    then
    echo file $1 deleted
else
    if rmdir $1
        then
        echo directory $1 deleted
    fi
fi

运行结果:

admin@server1:~$ ./delete hello2
file hello2 deleted
admin@server1:~$ ./delete hello2
rm: cannot remove `hello2': No such file or directory
rmdir: `hello2': No such file or directory
admin@server1:~$ mkdir hello3
admin@server1:~$ ./delete hello3
rm: cannot remove `hello3': Is a directory
directory hello3 deleted

下面是修复步骤:
1. 使用I/O重定向将结果保存到日志和错误文件中。
2. 捕获 rm 命令的返回值以生成成功或失败消息。
3. 捕获当前日期和时间并包含在输出日志中。

修复后的脚本 removefiles

#!/bin/bash
# removefiles deletes either files or directories
echo  "$0 ran at" $(date) >> delete.log
if rm $1 2>> delete-err.log
    then
    echo "deleted file $1" >> delete.log
elif rmdir $1 2>> delete-err.log
    then
    echo "deleted directory $1" >> delete.log
else
    echo "failed to delete $1" >> delete.log
fi

此脚本仍有不足,它未检查文件是否存在,也未区分文件和目录。可使用内置运算符进一步修复:

#!/bin/bash
# removefiles deletes either files or directories
echo  "$0 ran at" $(date) >> delete.log
if [ ! -e $1 ]
    then
    echo "$1 does not exist" >> delete.log
elif [ -f $1 ]
    then
    echo -n "file $1 " >> delete.log
    if rm $1 2>> delete-err.log
        then
        echo "deleted" >> delete.log
    else
        echo "not deleted" >> delete.log
    fi
elif [ -d $1 ]
    then
    echo "directory $1 " >> delete.log
    if rmdir $1 2>> delete-err.log
        then
        echo "deleted" >> delete.log
    else
        echo "not deleted" >> delete.log
    fi
fi

若文件名包含空格,需在脚本中再次对其加引号:

#!/bin/bash
# removefiles deletes either files or directories
echo  "$0 ran at" $(date) >> delete.log
if [ ! -e "$1" ]
    then
    echo "$1 does not exist" >> delete.log
elif [ -f "$1" ]
    then
    echo -n "file $1 " >> delete.log
    if rm "$1" 2>> delete-err.log
        then
        echo "deleted" >> delete.log
    else
        echo "not deleted" >> delete.log
    fi
elif [ -d "$1" ]
    then
    echo -n "directory $1 " >> delete.log
    if rmdir "$1" 2>> delete-err.log
        then
        echo "deleted" >> delete.log
    else
        echo "not deleted" >> delete.log
    fi
fi
4. 循环

若要多次执行某操作,需使用循环。Bash有三种循环: for while until

4.1 for循环
for arg in list
do
    commands
done

示例:

admin@server1:~$ for stooge in moe larry curly
> do
> echo $stooge
> done
moe
larry
curly
admin@server1:~$ for file in *
> do
> ls -l $file
> done
-rw-r--r--  1 admin admin 48 2006-08-26 14:12 hello
admin@server1:~$ for file in $(find / -name \*.gif)
> do
> cp $file /tmp
> done
4.2 while循环
while expression
do
    stuff
done

示例:

#!/bin/bash
MAX=100
((cur=1))  # Treat cur like an integer
while ((cur < MAX))
    do
    echo -n "$cur "
    ((cur+=1)) # Increment as an integer
    done
4.3 until循环
until expression
do
    stuff
done

示例:

#!/bin/bash
gameover="q"
until [[ $cmd == $gameover ]]
    do
    echo -n "Your commmand ($gameover to quit)? "
    read cmd
    if [[ $cmd != $gameover ]]; then $cmd; fi
    done

使用 break 可跳出循环,使用 continue 可跳过本次循环的剩余部分并回到循环开头。

5. cron作业

Shell脚本常用于组合程序,在Linux中,常见的用途是定义cron作业。cron是标准的Linux作业调度器,可让任务在指定时间执行。

编辑cron作业需编辑 crontab 文件,查看内容:

admin@server1:~$ crontab -l
no crontab for admin

编辑 crontab

admin@server1:~$ crontab -e

crontab 文件每行包含时间规范和命令,格式如下:
| 字段 | 范围 | 说明 |
| ---- | ---- | ---- |
| minute | 0 - 59 | 分钟 |
| hour | 0 - 23 | 小时(24小时制) |
| day_of_month | 1 - 31 | 每月的天数 |
| month | 1 - 12 或 月份名 | 月份 |
| day_of_week | 0 - 7(0或7为周日,6为周六)或 星期名 | 星期 |
| command | - | 要执行的命令 |

示例:

5 * * * * rm /tmp/*.gif # remove all GIF files every 5 minutes
5 * * * * rm -v /tmp/*.gif >> /tmp/gif.log # the same, logged

为避免收到cron作业的邮件通知,可将标准输出和标准错误重定向:

command > /dev/null 2>&1
6. 脚本语言对比

Shell主要用于运行命令和展开文件名模式,执行算术计算等其他任务较困难,因为需防止文本被分词和 * 展开。复杂的Shell脚本中,括号、方括号等符号会堆积如山。

如今,可使用更高级的脚本语言完成任务,原因如下:
- 一些应用(如 adduser apt-get )已自动化了传统的Shell脚本任务。
- Shell脚本扩展性差,难以维护。
- Shell脚本运行速度慢。
- Shell语法复杂。

接下来将使用不同语言编写一个应用,在 /etc/passwd 文件中搜索信息。

7. /etc/passwd 文件格式

/etc/passwd 文件通常包含系统账户、应用账户和用户账户,字段以冒号分隔:
| 字段 | 说明 |
| ---- | ---- |
| 账户名 | 账户的名称 |
| 加密密码 | 若使用 /etc/shadow ,则为 x |
| 用户ID(uid) | 用户的唯一标识 |
| 组ID(gid) | 用户所属组的标识 |
| 全名或描述 | 用户的全名或描述信息 |
| 主目录 | 用户的主目录路径 |
| Shell | 用户登录时使用的Shell |

我们主要关注第五个字段(全名或描述),在Unix中,该字段被称为 gecos 字段。

8. 脚本版本

下面将逐步编写在 /etc/passwd 文件中搜索信息的脚本。

8.1 简单的Bash脚本
#!/bin/bash
grep -i "$1" /etc/passwd

将此脚本命名为 finduser.sh 并使其可执行:

admin@server1:~$ chmod +x finduser.sh
admin@server1:~$ ./finduser.sh alf
adedarc:x:500:500:Alfredo de Darc:/home/adedarc:/bin/bash
8.2 限制搜索到 gecos 字段
#!/bin/bash
pattern=$1
IFS=":"
while read account password uid gid name directory shell
    do
    # Exact case-sensitive matches only!
    if [[ $name == $pattern ]]; then
        echo "$account:$password:$uid:$gid:$name:$directory:$shell"
        fi
        done < /etc/passwd
8.3 增加大小写不敏感匹配
#!/bin/bash
pattern=$1
IFS=":"
while read account password uid gid name directory shell
    do
    if [[ $(echo $name | egrep -i -c "$pattern") -gt 0 ]]; then
        echo "$account:$password:$uid:$gid:$name:$directory:$shell"
        fi
        done < /etc/passwd
8.4 限制搜索到用户ID大于500的账户
#!/bin/bash
pattern=$1
IFS=":"
while read account password uid gid name directory shell
    do
    # Exact matches only!
    if [[ $uid -gt 500 && $(echo $name | egrep -i -c "$pattern") -gt 0 ]]; then
        echo "$account:$password:$uid:$gid:$name:$directory:$shell"
        fi
        done < /etc/passwd

综上所述,通过本文我们了解了Bash脚本中算术运算、条件语句、循环、cron作业的使用,以及如何编写在 /etc/passwd 文件中搜索信息的脚本。同时,我们也看到了Shell脚本的局限性,以及使用更高级脚本语言的优势。在实际应用中,可根据具体需求选择合适的脚本语言和方法。

Bash脚本实用元素与脚本语言对比

9. 不同脚本语言的实现
9.1 Perl脚本

首先,我们来看一个简单的Perl脚本,用于在 /etc/passwd 文件中搜索指定字符串:

#!/usr/bin/perl
use strict;
use warnings;

my $pattern = $ARGV[0];
open(my $passwd_fh, '<', '/etc/passwd') or die "Can't open /etc/passwd: $!";
while (my $line = <$passwd_fh>) {
    if ($line =~ /$pattern/i) {
        print $line;
    }
}
close($passwd_fh);

操作步骤:
1. 将上述代码保存为一个 .pl 文件,例如 finduser_perl.pl
2. 给脚本添加执行权限: chmod +x finduser_perl.pl
3. 运行脚本: ./finduser_perl.pl alf

接下来,我们将搜索范围限制到 gecos 字段:

#!/usr/bin/perl
use strict;
use warnings;

my $pattern = $ARGV[0];
open(my $passwd_fh, '<', '/etc/passwd') or die "Can't open /etc/passwd: $!";
while (my $line = <$passwd_fh>) {
    my @fields = split(/:/, $line);
    my $gecos = $fields[4];
    if ($gecos =~ /$pattern/i) {
        print $line;
    }
}
close($passwd_fh);

最后,我们进一步限制搜索到用户ID大于500的账户:

#!/usr/bin/perl
use strict;
use warnings;

my $pattern = $ARGV[0];
open(my $passwd_fh, '<', '/etc/passwd') or die "Can't open /etc/passwd: $!";
while (my $line = <$passwd_fh>) {
    my @fields = split(/:/, $line);
    my $uid = $fields[2];
    my $gecos = $fields[4];
    if ($uid > 500 && $gecos =~ /$pattern/i) {
        print $line;
    }
}
close($passwd_fh);
9.2 PHP脚本

下面是一个简单的PHP脚本,用于在 /etc/passwd 文件中搜索指定字符串:

<?php
$pattern = $argv[1];
$passwd_file = file('/etc/passwd', FILE_IGNORE_NEW_LINES);
foreach ($passwd_file as $line) {
    if (stripos($line, $pattern) !== false) {
        echo $line . PHP_EOL;
    }
}
?>

操作步骤:
1. 将上述代码保存为一个 .php 文件,例如 finduser_php.php
2. 运行脚本: php finduser_php.php alf

将搜索范围限制到 gecos 字段的PHP脚本:

<?php
$pattern = $argv[1];
$passwd_file = file('/etc/passwd', FILE_IGNORE_NEW_LINES);
foreach ($passwd_file as $line) {
    $fields = explode(':', $line);
    $gecos = $fields[4];
    if (stripos($gecos, $pattern) !== false) {
        echo $line . PHP_EOL;
    }
}
?>

进一步限制搜索到用户ID大于500的账户的PHP脚本:

<?php
$pattern = $argv[1];
$passwd_file = file('/etc/passwd', FILE_IGNORE_NEW_LINES);
foreach ($passwd_file as $line) {
    $fields = explode(':', $line);
    $uid = $fields[2];
    $gecos = $fields[4];
    if ($uid > 500 && stripos($gecos, $pattern) !== false) {
        echo $line . PHP_EOL;
    }
}
?>
9.3 Python脚本

简单的Python脚本,用于在 /etc/passwd 文件中搜索指定字符串:

import sys

pattern = sys.argv[1]
with open('/etc/passwd', 'r') as f:
    for line in f:
        if pattern.lower() in line.lower():
            print(line.strip())

操作步骤:
1. 将上述代码保存为一个 .py 文件,例如 finduser_python.py
2. 运行脚本: python finduser_python.py alf

将搜索范围限制到 gecos 字段的Python脚本:

import sys

pattern = sys.argv[1]
with open('/etc/passwd', 'r') as f:
    for line in f:
        fields = line.strip().split(':')
        gecos = fields[4]
        if pattern.lower() in gecos.lower():
            print(line.strip())

进一步限制搜索到用户ID大于500的账户的Python脚本:

import sys

pattern = sys.argv[1]
with open('/etc/passwd', 'r') as f:
    for line in f:
        fields = line.strip().split(':')
        uid = int(fields[2])
        gecos = fields[4]
        if uid > 500 and pattern.lower() in gecos.lower():
            print(line.strip())
10. 不同脚本语言的优缺点对比
脚本语言 优点 缺点
Bash - 与系统命令集成良好,适合简单的系统管理任务。
- 无需额外安装,在Linux系统中广泛存在。
- 算术运算和复杂逻辑处理较困难。
- 扩展性和可维护性较差。
Perl - 强大的文本处理能力,正则表达式支持优秀。
- 有丰富的模块库。
- 语法相对复杂,代码可读性可能较差。
PHP - 易于学习和上手,有大量的内置函数。
- 适合Web开发和简单的脚本任务。
- 主要用于Web环境,在系统管理方面的功能相对较弱。
Python - 语法简洁,代码可读性高。
- 有丰富的第三方库,可用于各种领域。
- 运行速度相对较慢。
11. 总结与建议

在选择脚本语言时,需要根据具体的任务需求来决定:
- 如果是简单的系统管理任务,如文件操作、进程管理等,Bash脚本是一个不错的选择。
- 对于复杂的文本处理和正则表达式匹配,Perl可能更合适。
- 如果任务与Web开发相关,或者需要快速实现一个简单的脚本,PHP是一个好的选择。
- 对于数据处理、机器学习等复杂任务,Python是首选,其丰富的库可以大大提高开发效率。

通过本文,我们详细了解了Bash脚本的各种实用元素,包括算术运算、条件语句、循环、cron作业等,同时对比了不同脚本语言在处理 /etc/passwd 文件搜索任务时的实现方式和优缺点。希望这些内容能帮助你在实际应用中选择合适的脚本语言和方法。

下面是一个简单的mermaid流程图,展示了在 /etc/passwd 文件中搜索信息的基本流程:

graph TD;
    A[开始] --> B[读取搜索模式];
    B --> C[打开 /etc/passwd 文件];
    C --> D[逐行读取文件];
    D --> E{是否匹配模式};
    E -- 是 --> F[输出匹配行];
    E -- 否 --> D;
    D --> G{是否到达文件末尾};
    G -- 否 --> D;
    G -- 是 --> H[关闭文件];
    H --> I[结束];

这个流程图清晰地展示了在 /etc/passwd 文件中搜索指定字符串的基本步骤,从读取搜索模式开始,逐行检查文件内容,输出匹配的行,直到文件结束。

本地跟单专家顾问(EA)是一种专为MetaTrader 4平台设计的自动化交易工具。该版本强调其无限制特性,允许用户在任何时段、不同地理区域及各类账户上自由部署,从而为交易者提供了高度灵活的操作空间。其核心机制采用同向复制策略,即接收端会完全模仿发送端的交易方向操作,适合那些信赖信号源稳定性的用户,以期通过跟随策略实现相近的投资回报。 系统架构包含两个独立模块:信号发送端信号接收端。发送端安装于主导交易决策的账户,接收端则配置于需同步执行的账户,二者协同工作,实现了交易指令的自动传递执行,有效减少了人工干预的需求。此外,该工具特别注重MT4服务器时间的同步,确保交易执行时点的精确性,避免因时区偏差可能引发的操作失误,这对于依赖时间敏感性的外汇市场尤为重要。 文件标识中的特定代号可能指向开发者的内部版本标记或某种定制化交易逻辑,具体含义需结合进一步的技术文档予以确认。整体而言,该EA为多账户管理策略复制提供了一个集成化解决方案,有助于提升交易执行的效率并降低操作风险。但需注意,市场环境处于持续变动中,任何自动化工具均需经过充分验证适应性测试,历史表现不能作为未来收益的保证。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值