0%

本文主要参考自[Wiki] How to contribute to Manjaro ARM

编译平台

运行Manjaro的x86_64或aarch64设备,需要安装Manjaro-arm-tools。鉴于R3300-M已经运行了Manjaro ARM 21.08,所以以下用盒子制作盒子镜像。

sudo pacman -S manjaro-arm-tools

获取配置文件

sudo getarmprofiles -f

修改配置文件

manjaro-arm-tool会从服务器下载现成的rootfs压缩包(大概160MB),然后以此为基础根据配置文件进行额外的包安装及配置,最终生成压缩镜像。笔者以vim2为模板进行修改:

  1. 进入配置文件目录:/usr/share/manjaro-arm-tools/profiles/arm-profiles/devices

  2. 复制vim2配置文件:cp vim2 vim2m

  3. 修改vim2m:

## Maintained by Spikerguy ##

# Kernel and bootloader stuff
linux-aml
boot-vim2
plymouth
plymouth-theme-manjaro

# Video driver

# Other device specific packages
crda
btrfs-progs
#bluetooth-vim3
#khadas-utils
fbset
#kvim1-firmware
#kvim2-firmware
#ap6398s-firmware
vim2-post-install

最主要的修改是把内核从linux换成了linux-aml,之前实测过5.13版本主线内核无法启动R3300-M,linux-aml正常。其他几个固件估计R3300-M也用不上,故注释掉。

  1. 生成镜像
sudo buildarmimg -d vim2m -e minimal -v 21.08 -n

基本参数解释:

Usage: buildarmimg [options]
    -d <device>        Device the image is for. [Default = rpi4. Options = oc2, on2, on2-plus, pbpro, pine64, pine64-lts, pinebook, pinephone, pinetab, rock64, rockpi4, rockpro64, rpi3, rpi4, vim1, vim2, vim3]
    -e <edition>       Edition of the image. [Default = minimal. Options = cubocore, gnome, i3, kde-plasma, lxqt, mate, minimal, plasma-mobile, server, wayfire, xfce]
    -v <version>       Define the version the resulting image should be named. [Default is current YY.MM]
    -i <package>       Install local package into image rootfs.
    -b <branch>        Set the branch used in the image. [Default = stable. Options = stable, testing or unstable]
    -n                 Force download of new rootfs.
    -x                 Don't compress the image.
    -h                 This help

本例用刚才制作的vim2m配置文件生成了minimal镜像。

生成镜像不会耗时很多,因为涉及的主要是打包和镜像构建,不需要从源码开始编译。我这里测试是16.58分钟,建议优化网络否则下载Manjaro-ARM-aarch64-latest.tar.gz(约160M)这一步会耗时很久。

  1. 复制镜像到电脑

进入镜像目录:cd /var/cache/manjaro-arm-tools/img

复制镜像到本地家目录:scp Manjaro-ARM-minimal-vim2m-2108img.xz tccmu@192.168.1.221:/home/tccmu

tccmu是电脑上当前用户名,替换成自己的即可。192.168.1.221是自己电脑的IP(不是盒子),同样要替换。

  1. 写入镜像到TF卡:
    xz -dc Manjaro-ARM-minimal-vim2m-2108.img.xz | dd of=/dev/sdX bs=1M status=progress conv=fsync
    
    写入设备名自己替换。

之后的配置没有任何难度了,修改extlinux.conf使用合适dtb,修改u-boot-s905为u-boot.ext就完事大吉。

备注

目前镜像已生成完毕,但未经实机测试。

更新

测试生成的镜像无法启动,连u-boot都进不去,直接进入盒子安卓系统的recovery。怀疑是rootfs包下载错误问题。

另外最新发布的21.08经测试可以正常启动运行,其搭载的主线内核 5.13.12-1-MANJARO-ARM #1 SMP Wed Aug 18 07:36:58 UTC 2021 aarch64 GNU/Linux一切正常,所以替换aml内核貌似就没必要了。

部分linux发行版网络配置GUI强制openconnect使用密钥,用户名-密码登录可以通过命令行实现:

sudo openconnect [域名或IP地址]:[端口号]

然后根据提示操作就可以了。

主要来源:在 Manjaro Linux 系统使用 Docker

提要

硬件平台还是百视通R3300-M …… 安装Manjaro Arm 21.04后已升级到21.08,内核版本 5.13.0-1-MANJARO-ARM #1 SMP PREEMPT Wed Jun 30 23:07:51 +03 2021 aarch64 GNU/Linux

安装Docker

sudo pacman -Syu
sudo pacman -S docker

启动服务

sudo systemctl start docker.service

添加到系统启动项

sudo systemctl enable docker.service

添加当前用户到Docker组

这样当前用户就有足够权限操作docker而不必sudo权限。

sudo usermod -aG docker $USER

换国内源

打开或创建 /etc/docker/daemon.json 文件:

{
    "registry-mirrors": [
        "https://registry.docker-cn.com"
    ]
}

registry.docker-cn.com 是 Docker 的官方中国镜像, 除此之外还有其他一些第三方镜像可选:

镜像 地址
Azure中国 https://dockerhub.azk8s.cn
中科大 https://docker.mirrors.ustc.edu.cn
七牛云 https://reg-mirror.qiniu.com
网易云 https://hub-mirror.c.163.com
腾讯云 https://mirror.ccs.tencentyun.com

保存文件之后重启一下 Docker 服务:

sudo systemctl daemon-reload
sudo systemctl restart docker

其他操作

docker search redis
docker pull redis
docker pull redis:rc
docker images
docker run --name myredis -d redis
docker ps
docker stop myredis
docker rm myredis
docker info

安装Portainer CE

docker volume create portainer_data
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce

下步计划

之前在docker里运行过dokuwiki,tiddlywiki,minecraft server等等。跑minecraft server性能不太行,破坏方块有明显卡顿。

生成密钥及配置(略)

网上教程有很多

生成站点并推送到github

hexo g -d

推送站点源码到github

.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
git add .
git commit -m 'hexo 源文件推送'
git push origin hexoSource

远程分支名用自己的

拉取源码到本地

git clone -b blogSource https://github.com/12321/12321.github.io.git

参考:manjaro 切换国内源及软件安装

同步时间

时间不准会造成SSL验证失败等一系列问题。同步方式如下:(https://wiki.manjaro.org/index.php/System_Maintenance#Time_and_Date)

user $ timedatectl set-ntp true
user $ sudo systemctl enable --now systemd-timesyncd

当然也可以用date手动设置。

换国内源

sudo pacman-mirrors -i -c China -m rank
sudo pacman-mirrors -g
sudo pacman -Syyu

安装ARU包管理工具

sudo pacman -S yay

原本想通过ARU安装RTL8189ETV启动,但是包依赖linux mainline kernel,貌似不行。手动编译也没成功。

pacman.conf选项自定义

添加了Color和ILoveCandy选项,第一个使pacman产生彩色输出,第二个把pacman下载进度条变成吃豆子,虽然没什么用还是设置了。

pacman常用命令

参考:Pacman Overview

  1. 安装更新

To update the package database and update all packages on the system

user $ sudo pacman -Syu

To force a full refresh of the package database and update all packages on the system. You must do this when switching branches or switching mirrors.

user $ sudo pacman -Syyu

To force a full refresh of the package database, update all packages on the system and allow packages to be downgraded. Downgrading should be only be needed when switching to an older branch. For example, switching from Testing to Stable.

user $ sudo pacman -Syyuu
  1. 搜索软件包

To search the Manjaro repositories for available packages you can use the command pacman -Ss keyword. It will search both the package name and the description for the keyword. For example, to search for packages containing the keyword smplayer you could use:

user $ pacman -Ss smplayer

You can search your installed packages in the same manner using -Qs instead of -Ss. To search your installed packages for smplayer:

user $ pacman -Qs smplayer

Once you have found a package you can use pacman -Qi to get more information about an installed packages or pacman -Si for packages in the repos. Following the example above you could use

user $ pacman -Si smplayer

Finally, for a list of all installed packages on your system, enter the following command:

user $ pacman -Ql

  1. 安装软件包
    To install a software package, the basic syntax is pacman -S packagename. However, installing a package without updating the system will lead to a partial upgrade situation so all the examples here will use pacman -Syu packagename which will install the package and ensure the system is up to date. For example, to install smplayer the command is:

    user $ sudo pacman -Syu smplayer

You will then be presented a list of software to install. You may notice this list has more packages than you requested. This is because many packages also have dependencies which are packages that must be installed in order for the software you selected to function properly.

Pacman can also directly install packages from the local system or a location on the internet. The format of that command is pacman -U packagelocation. For example, to install a copy of your package cache you could do something like:

user $ sudo pacman -U /var/cache/pacman/pkg/smplayer-19.5.0-1-x86_64.pkg.tar.xz

Alternatively, you could get it directly from one of Manjaro’s mirrors:

user $ sudo pacman -U https://mirror.alpix.eu/manjaro/stable/community/x86_64/smplayer-19.5.0-1-x86_64.pkg.tar.xz
  1. 删除软件包
    To remove a software package, the basic syntax is sudo pacman -R packagename. We could remove the smplayer package we installed above with:

    user $ sudo pacman -R smplayer

This will remove the package, but will leave all the dependencies behind. If you also want to remove the unneeded dependencies you could use pacman -Rsu packagename as seen in this example:

user $ sudo pacman -Rsu smplayer

Sometimes when you try to remove a package you will not be able to because there are other packages which depend on it. You can use pacman -Rc packagename to remove a package and everything that depends on it. Be careful to heed the above warning when using this option.

user $ sudo pacman -Rc smplayer

The most nuclear option is pacman -Rcs packagename. This will remove everything that depends on packagename and continue to do so on its dependencies. This should really only be used in exceptional circumstances such as when removing an entire desktop environment and trying not to leave anything behind.

Pacman usually also creates backup configuration files when deleting packages. To remove those, you can add n to any of the examples above. For example:

user $ sudo pacman -Rn smplayer
user $ sudo pacman -Rsun smplayer
user $ sudo pacman -Rcn smplayer

提要

这个盒子硬件没啥好说的了,前面介绍过。之前用它运行Armbian 20.10 Focal 没问题,但是balbes150弃坑后无人接手,前景不明,于是迁移到了Manjaro ARM。目前来看社区支持比较完善,更新及时,测试内核5.13(linux-aml)运行成功。

镜像

选择了Manjaro ARM 21.04。为什么没有选择更新的版本,比如21.06,还得从内核提起。21.04搭载主线5.11内核,测试启动R3300-M没问题,但是后续版本搭载的主线内核(测试了5.13)无法启动,提示phy phy-c000000.phy.0:phy poweron failed --> -22然后卡死,所以需要想办法卸载主线内核然后安装Amlogic分支。

https://github.com/manjaro-arm/vim2-images/releases/download/21.04/Manjaro-ARM-minimal-vim2-21.04.img.xz
https://github.com/manjaro-arm/vim2-images/releases/download/21.04/Manjaro-ARM-mate-vim2-21.04.img.xz
https://github.com/manjaro-arm/vim2-images/releases/download/21.04/Manjaro-ARM-kde-plasma-vim2-21.04.img.xz/
https://github.com/manjaro-arm/vim2-images/releases/download/21.04/Manjaro-ARM-xfce-vim2-21.04.img.xz/

官方提供的4个镜像中,minimal不含图形界面,经本人测试运行成功,其余带图形界面的镜像未经测试。

安装

和Armbian 20.10 Focal基本一致,修改extlinux.conf选择合适dtb(R3300-M使用meson-gxbb-p201.dtb),修改u-boot-s905为u-boot.ext(镜像里默认的u-boot.ext适用s905x和s912,不适用于R3300-M),然后启动盒子,完成设置,SD卡自动扩容,一切结束后自动重启完成安装进入系统。

系统更新(更换内核)

冻结内核

首先修改系统文件避免更新内核(现在更新系统就挂了)。所有Manjaro发行版均预装nano,所以:

sudo nano /etc/pacman.conf

找到IgnorePkg一行,取消开头注释,修改为IgnorePkg = linux,保存退出,执行sudo pacman -Syu更新系统。系统更新后,建议重启系统。

切换内核为linux-aml

执行

sudo pacman -S linux-aml

这步会删除主线内核,安装更合适的Amlogic分支内核。结束后重启系统,用uname -a查看,系统内核已更新为5.13。

收尾工作

再次编辑/etc/paman.conf,修改IgnorePkg = linuxIgnorePkg = linux-aml,毕竟在电视盒子这种非正式支持的设备上更新内核还是要谨慎一些,没必要别乱动内核。确实需要更新的情况下,提前备份好Image和initramfs-linux.img,方便回滚。

备注

  1. 经测试,系统写入emmc无法启动。
  2. 如果使用U盘启动,需要在extlinux.conf中指定usb-storage.quirks参数。例如:

APPEND root=PARTUUID=5418e4d8-02 rootflags=data=writeback rw console=ttyAML0,115200n8 console=tty0 no_console_suspend consoleblank=0 fsck.fix=yes fsck.repair=yes net.ifnames=0 quiet splash plymouth.ignore-serial-consoles usb-storage.quirks=152d:1561:u

152d:1561为U盘vendor和product的ID号码。在linux下可以使用lsusb查看,Windows下也有类似小工具。
3. 无法使用root身份SSH登录,会提示密码错误。建议用普通用户连接后再切换身份。
4. 有线网卡Mac地址不固定,每次重启都会变化。
5. WIFI暂不可用,待进一步测试。
6. 类似21.06等感觉可以通过替换Image和initramfs-linux.img实现从内核从主线到Amlogic分支切换,有兴趣的可以测试。

pdftk删除页码脚本

最近把《Linux命令行与shell脚本编程大全》翻了一遍,想演练一下,于是写出了下面这个脚本,用于调用pdftk删除给定页码生成新文档。编写调试过程中,对bash函数传参、位置参数调用等概念有了更深入的理解,特别是shift,用于处理数量未知的位置参数格外好用。

#!/bin/bash
# 对pdftk的简单封装,用于删除指定的页码,以空格分隔,支持形如"5-7"的页码范围。页码输入不必按顺序,类似"12 6-8 1 3"输入是可以正常工作的。

#isPdftkinstalled作用为检查pdftk是否可用,若可用则执行pdftk --version
function isPdftkinstalled {
    echo
    if [ -z $(whereis pdftk | gawk '{print $2}') ]
    then
        echo "pdftk未安装或未加入PATH,请检查。"
        echo "提示:pdftk已加入Deepin官方源,您可以通过 sudo apt install pdftk 简单安装。"
    else
        echo $(pdftk --version)
    fi
    echo
}

#getCouples作用为对输入的页码进行处理,支持输入单页或页码范围,将其转换为数对(获取范围前后页码,以冒号分隔)
function getCouples {
    local couples=''
    local left_end=''
    local right_end=''
    while [ -n "$1" ]
    do      
        arg1=$(echo "$1" | gawk -F"[- ]" '{print $1}')
        arg2=$(echo "$1" | gawk -F"[- ]" '{print $2}')
        left_end=$[ $arg1 - 1 ]
        #对应页码范围情况
        if [ -n "$arg2" ]
        then
            right_end=$[ $arg2 + 1 ]
        else
        #对应单页情况
            right_end=$[ $arg1 + 1 ]
        fi
        couples=$(echo $couples $left_end:$right_end)
        shift
    done
    #数对排序处理,注意sort是针对行的排序,故需要将空格转换成换行
    couples=$(echo $couples | tr " " "\n" | sort -t ':' -k 1 -n)
    couples=$(refineCouples $couples)
    echo $couples
}

#refineCouples作用为合并相邻数对,供getCouples调用
function refineCouples {
    local args=''
    local isNabour=''
    local Num1=$(echo $1 | cut -d ":" -f1)
    local Num2=$(echo $1 | cut -d ":" -f2)
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=$(echo $1 | cut -d ":" -f1)
        Num4=$(echo $1 | cut -d ":" -f2)
        isNabour=$[ $Num2 - $Num3 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1:$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
    done
    args=$(echo $args $Num1:$Num2)
    echo $args
}

#generateRanges作用为将数对转换为pdftk可用的页码范围
function generateRanges {
    local first_Bit=$(echo $1 | cut -d ":" -f1)
    local Num1=''
    local Num2=''
    local args=''
    while [ -n "$1" ]
    do
        Num1=$(echo $1 | cut -d ":" -f2)
        if [ -n "$2" ]
        then
            Num2=$(echo $2 | cut -d ":" -f1)
            if [ $Num1 -gt $Num2 ]
            then
                Num2=$Num1
            fi
        else
            Num2="end"
        fi
        shift
        args=$(echo $args $Num1-$Num2)
    done

    if [ $first_Bit -gt 0 ]
    then
        args=$(echo 1-$first_Bit $args)
    fi
    echo $args
}

#此函数作用为合并相邻页码范围,在使用refineCouples后已无使用必要,可删除
function refineRanges {
    local args=''
    local isNabour=''
    local Num1=$(echo $1 | cut -d "-" -f1)
    local Num2=$(echo $1 | cut -d "-" -f2)
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=$(echo $1 | cut -d "-" -f1)
        Num4=$(echo $1 | cut -d "-" -f2)
        isNabour=$[ $Num3 - $Num2 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1-$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
        echo $Num1
    done
    args=$(echo $args $Num1-$Num2)
    echo $args   
}

pdfFile=$1
shift
if [ $# -lt 2 ]
then
    echo "Usage:pdftool [filename] [discard pages]"
    exit
fi

pagesConserved=$(generateRanges $(getCouples $*))
pdftk_command=$(echo pdftk $pdfFile cat $pagesConserved output new_$(basename ${pdfFile}))
#echo $pdftk_command
$pdftk_command

根据大佬指点,使用bash自身的字符串展开替代cut命令:

#!/bin/bash
# 对pdftk的简单封装,用于删除指定的页码,以空格分隔,支持形如"5-7"的页码范围。页码输入不必按顺序,类似"12 6-8 1 3"输入是可以正常工作的。

#isPdftkinstalled作用为检查pdftk是否可用,若可用则执行pdftk --version
function isPdftkinstalled {
    echo
    if [ -z $(whereis pdftk | gawk '{print $2}') ]
    then
        echo "pdftk未安装或未加入PATH,请检查。"
        echo "提示:pdftk已加入Deepin官方源,您可以通过 sudo apt install pdftk 简单安装。"
    else
        echo $(pdftk --version)
    fi
    echo
}

#getCouples作用为对输入的页码进行处理,支持输入单页或页码范围,将其转换为数对(获取范围前后页码,以冒号分隔)
function getCouples {
    local couples=''
    local left_end=''
    local right_end=''
    while [ -n "$1" ]
    do      
        arg1=$(echo "$1" | gawk -F"[- ]" '{print $1}')
        arg2=$(echo "$1" | gawk -F"[- ]" '{print $2}')
        left_end=$[ $arg1 - 1 ]
        #对应页码范围情况
        if [ -n "$arg2" ]
        then
            right_end=$[ $arg2 + 1 ]
        else
        #对应单页情况
            right_end=$[ $arg1 + 1 ]
        fi
        couples=$(echo $couples $left_end:$right_end)
        shift
    done
    #数对排序处理,注意sort是针对行的排序,故需要将空格转换成换行
    couples=$(echo $couples | tr " " "\n" | sort -t ':' -k 1 -n)
    couples=$(refineCouples $couples)
    echo $couples
}

#refineCouples作用为合并相邻数对,供getCouples调用
function refineCouples {
    local args=''
    local isNabour=''
    #local Num1=$(echo $1 | cut -d ":" -f1)
    #local Num2=$(echo $1 | cut -d ":" -f2)
    local Num1=${1%%:*}
    local Num2=${1##*:}
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=${1%%:*}
        Num4=${1##*:}
        isNabour=$[ $Num2 - $Num3 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1:$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
    done
    args=$(echo $args $Num1:$Num2)
    echo $args
}

#generateRanges作用为将数对转换为pdftk可用的页码范围
function generateRanges {
    local first_Bit=${1%%:*}
    local Num1=''
    local Num2=''
    local args=''
    while [ -n "$1" ]
    do
        Num1=${1##*:}
        if [ -n "$2" ]
        then
            Num2=${2%%:*}
            if [ $Num1 -gt $Num2 ]
            then
                Num2=$Num1
            fi
        else
            Num2="end"
        fi
        shift
        args=$(echo $args $Num1-$Num2)
    done

    if [ $first_Bit -gt 0 ]
    then
        args=$(echo 1-$first_Bit $args)
    fi
    echo $args
}

pdfFile=$1
shift
if [ $# -lt 2 ]
then
    echo "Usage:pdftool [filename] [discard pages]"
    exit
fi

pagesConserved=$(generateRanges $(getCouples $*))
pdftk_command=$(echo pdftk $pdfFile cat $pagesConserved output new_$(basename ${pdfFile}))
#echo $pdftk_command
pdftk_command

可见使用bash本身的字符串替换更加高效灵活,通过#或%的数量可以制定首次匹配或最长匹配,用来截取文件名或后缀很有用。

bash递归函数测试

#!/bin/bash
# using recursion

function factorial {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -eq 1 ]
    then
        echo 1
    else
        local temp1=$1
        local temp2=$(factorial $[ $1 - 1 ])
        local result=$[ $temp1 * $temp2 ]
        echo $result
    fi
}

function sumn {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -eq 1 ]
    then
        echo 1
    else
        local temp1=$1
        local temp2=$(sumn $[ $1 - 1 ])
        local result=$[ $temp1 + $temp2 ]
        echo $result
    fi
}

function fbnq {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -lt 2 ]
    then
        echo 1
    else
        local temp1=$(fbnq $[ $1 - 1 ])
        local temp2=$(fbnq $[ $1 - 2 ])
        local result=$[ $temp1 + $temp2 ]
        echo $result
    fi
}

read -p "Enter value: " value
result=$(factorial $value)
totalsum=$(sumn $value)
fibon=$(fbnq $value)
echo "The factorial of $value is: $result"
echo "The total sum of $value is: $totalsum"
echo "FibonacciRecursive of $value is: $fibon"

递归实现只有2步:构造递归表达式以及设定初值。

bash中简单函数数组传递

#!/bin/bash
# array variable to function test
function testit {
local newarray
newarray="$@"
echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
testit ${myarray[*]}

书中使用命令展开进行传值

newarray=($(echo "$@"))

经过试验,这种方式也可以:

newarray=("$@")

从输出来看一致,不过内在是否存在区别?

我自己来解答:

  1. newarray="$@" : newarray不是数组;
  2. newarray=("$@") : newarray是数组(套了圆括号)
  3. newarray=("$*") : newarray不是数组

对于第3点可以回顾一下$@$*的区别。$@中每个元素是独立的,$*只有一个元素。另外无论newarray是否为数组,${newarray[*]}都可以完整输出内容,因为当newarray不是数组的情况下,全部内容都在${newarray[0]}中,从标准输出来看没区别。

对于for而言数组与否没区别,它只会根据当前IFS分割元素。

再次回顾书中的方式:

newarray=($(echo "$@"))

这种方式看似愚笨,实则非常鲁棒。通过命令展开将不确定的输入类型输出成普通字符串,再套括号变成数组,值得品味。

bash中简单的bc计算

#!/bin/bash

var1=3.14
var2=5.2

var3=$(bc << EOF
scale = 4
a1 = $var1 + $var2
a2 = $var1 * $var2
a1 + a2
EOF
)

echo The final result is $var3

结果毫无悬念,24.668。注意这里用到了here document语法,行内重定向的一大用途便是方便在行间传递参数调用程序,通常搭配命令替换使用。另外注意,bc中引用外部变量(bash变量)时需要加$,bc中声明的变量是不需要的。