管道

管道的使用例子,在Linux shell中使用的非常多,本文只能简单的挑一下常用的,或者具有启发性的例子,记录有效性。

例子

从heredoc中获取输入,提供给交互式输入

交互式的shell直接从heredoc中获取输入,但是需要注意末尾的EOL不能多隐藏的空格等符号。

LIMIT=2
psql -h localhost -U gpadmin -d develop << EOL
\timing on
select ship_id  from tb_scan limit $LIMIT;
-- 还可以有其他的操作等
select doc_sn form tu_doc_info limit $LIMIT;

EOL

由此,我可以想象,mysql等交试工具 也是可以的。

从文件获取

psql -h localhost -U gpadmin -d develop < some.sql

clickhouse的例子


## 创建表
clickhouse-mysql \
 --src-host=127.0.0.1 \
 --src-user=reader \
 --src-password=Qwerty1# \
 --table-templates-with-create-database \
 --src-table=airline.ontime > create_clickhouse_table_template.sql

## 修改脚本
vim create_clickhouse_table_template.sql

## 导入建表
clickhouse-client -mn < create_clickhouse_table_template.sql

从管道中获取

cat some.sql | psql -h localhost -U gpadmin -d develop

利用提供的命令

psql -h localhost -U gpadmin -d develop -f some.sql

psql -h localhost -U gpadmin -d develop -c "select * from some table ....."

定义多行的文本

query_sql是定义的多行文本,query_sql中的换行符号,会被换成一行。坑:*会被转义为具体的文件列表。

sql中也不能有解释,因为在转换成一行的过程中,会混在一起,导致失效。

  • 方式一,cat+heredoc 来定义多行文本

    多行文本,会被转换为单行。丢失原来的换行符号。故,如果有注释,则必须要删除。

query_sql=$(cat <<EOL
SELECT
    scan_site,
    count(*) as cnt
FROM
    (
        SELECT
            ship_id,
            scan_site,
            ROW_NUMBER () OVER (
                PARTITION BY ship_id
                ORDER BY
                    ins_db_tm DESC
            ) AS rn
        FROM
            tb_scan
        WHERE
            ins_db_tm >= '#START_DATE'
        AND ins_db_tm < '#END_DATE'
        AND scan_typ = '10'
        AND rmk_id = '1'
    ) AS T
WHERE
    T.rn = 1
GROUP BY scan_site
order by cnt desc;
EOL
)

query_sql=$(
    echo "$query_sql" |
    sed "s/#START_DATE/$START_DATE/g"|
    sed "s/#END_DATE/$END_DATE/g" |
    sed "s/#SCAN_SITE/$SCAN_SITE/g"
)

psql -h localhost -U gpadmin -d develop -e -c "$query_sql"

注意,上面也有sed的多行写法的范例。

备注:query_sql=$(cat <<EOL这种方式定义的多行文本,在echo的时候,变量需要加双引号,否则,会变成单行的文本。

  • 方式二,双引号等,也能定义多行文本
SQL="
with tb as (select * from tb_scan where ins_db_tm > '${DAY_AGO_10}' and ship_id = '${SHIP_ID}' )
select * from tb_scan where ins_db_tm > '${DAY_AGO_10}' and ship_id in (select grp_ship_id from tb) union all select * from tb order by ins_db_tm; 
"
echo $SQL # echo 就会将*替换调,不echo 就没事
  • 方式三,read到变量中,等同于方式一。

    多行文本,会被转换为单行。

    read -d "" fcyf_some_sql << EOF
    select
        count(*) as 总数,
        
    xxx kjkdf
    EOF
    
    echo $fcyf_some_sql
  • 极简方式:

    懒得再定义,直接在命令行中写要执行的sql。

psql -h localhost -U gpadmin -d develop -e -c " select count(*)  from tu_doc_info where dtime >= '$START_DATE' and dtime < '$END_DATE'  "

# 其实clickhouse也是差不多的

clickhouse-client --query "CREATE DATABASE IF NOT EXISTS tutorial"

cat + heredoc

  • 写入文本

    比如,没有vi的时候,甚至,因为vi复制粘贴的时候,会多出换行符号,都可以使用该方式来写入到文件。

cat > target.txt <<EOL
a
bb dafadsf
EOL

# - EOL  去掉前导的空白符号等
# 'EOL'  不解析变量
cat <<EOL  > target.txt 
EOL
  • 先处理,再写入到文件
cat <<EOL |sed 's/^/you-get -l /'  > target.txt 
http://url.com/1
http://url.com/1
EOL

while + read

具体参考文章。

echo 方式

通过管道,交给交互式shell中

echo "SELECT * FROM system.numbers LIMIT 10000000 OFFSET 10000000" | clickhouse-benchmark -i 10

说说echo的坑

# 正常 
echo "SELECT * FROM system.numbers LIMIT 10000000 OFFSET 10000000"
# 赋值给变量
SQL="SELECT * FROM system.numbers LIMIT 10000000 OFFSET 10000000"

# 上面的 * 别解析成了具体的 文件列表。
echo $SQL
# 为了不被解析,应将上述变量,包括在括号内
echo "$SQL"

坑:echo 后面的变量,如果缺少了双引号包裹,则,变相的,拆分成了n个参数,echo * 确实会输出文件列表。

sh + heredoc

以下是综合上面的例子,使用到sh命令行上的例子。shell也是交互式的,通过,heredoc方式,让多行的代码,更容易理解一点。但是,需要注意,heredoc中的变量不要被提前解析。

  • 方式一
cat <<'EOL' |sh
for var in {1..10};do
    echo  $var 
done
EOL
  • 方式二
sh <<'EOL'
for var in {1..10};do
    echo  $var 
done
EOL
  • 方式3
echo '
for var in {1..10};do
    echo  $var 
done ' |sh
  • 错误

    原因说明:有效果,但是输出的都是空。为啥?因为$var 在EOL阶段被解析了,在传入到sh中,即为空字符串了。

sh <<EOL
for var in {1..10};do
    echo  $var 
done
EOL
  • 其实想多了

    在shell模式下,输出以下内容,换行的时候,其实,shell能正确的识别内容还没有输入完,还接着让你输入。

for each in ./*;do

总结

管道符号 |

多个进程之间,使用该方式来传递数据。多个程序,程序之间没有缓存,如果其中一个进程卡了一会,貌似其他进程也会卡着。可以使用多次。

输出重定向符号 >

输出到文件,一般是放到最后的那条命令,唯一的例外,遇到heredoc方式,可能并不

输出重定向符号<

从文件中读取命令,放到交互试shell中,则可以使用该方式。其实跟heredoc还是挺像的。

heredoc符号 <<EOL

直接在heredoc中写文本,供交互式shell来使用。

特别说明:heredoc这种模式,必须用在命令行后面,这样shell才会识别,并且将后面的内容,作为输入的源。最好,直接跟在原始的命令行后面。

一个错误的示例如下:

因为其不是直接跟在命令行后面的。不会被识别。

MY_VAR=<<EOL
这里面的内容是不会赋值给变量,MY_VAR中的。
EOL

纠正上面的错误:

稍微注意一点的是:结束符号,EOL后面不能跟任何内容。否则,不能正常的识别。(为了满足,跟在命令行后面,刻意加的cat命令)

MY_VAR=$(cat<<EOL
这里面的内容是不会赋值给变量,MY_VAR中的。
EOL
)

如果,混合的其他的管道符号,<<EOL必须紧跟在命令行后面。

顺序问题

只遇到过heredoc+cat+管道的例子。参照前面