博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
expansions and substitutions
阅读量:6947 次
发布时间:2019-06-27

本文共 13296 字,大约阅读时间需要 44 分钟。

  hot3.png

How bash does expansions and substitutions?

    Before executing your commands,bash checks whether there are any syntax elements in the command line that should be interpreted rather than taken literally.After splitting the command line into tokens(words),bash scans for these special elements and interprets them,resulting in a changed command line:the elements are said to be expanded or subistituted to new text and maybe new tokens(words).

    Bash performs expansions and substitutions in a defined order.This explains why globbing(pathname expansion),for example,is safe to use on filenames with spaces(becasue it happens after the final word splitting).

    The order is (from first to last):

  1. brace expansion
  2. tlide expansion
  3. parameter expansion    arithemetic expansion    command expansion
  4. word spliting
  5. pathname expansion

    Process substitution is performed simultaneously with parameter expansion,command substitution and arithmetic expansion.It is only performed when the underlying operating system supports it.

    The 3 steps parameter expansion, arimethic expansion and command expansion happen at the same time in a left-to-right fashion on the commandline.This means:

i=1

echo $i $((i++)) $i

will output 1 1 2 not 1 1 1

expansions and substitutions in details

What is brace expansion?

{string1,string2,......,stringN}

{<START>..<END>}

{<START>..<END>..<INCR>}(bash 4)

<PREAMBLE>{........}

{........}<POSTSCRIPT>

<PREAMBLE>{........}<POSTSCRIPT>

    Brace expansion is used to generate arbitrary strings.The specified strings are used to generate all possible combinations with the optional surrounding preambles and postscripts.

    Usually it's used to generate mass-arguments for a command,that follow a specific naming-scheme.

    It is the very first step in expansion-handling,it's important to understand that.When you use

echo {a,b}$PATH

    then the brace expansion does not expand the variable -- this is in a later step.Brace expansion just makes it being:

echo a$PATH b$PATH

    Another common pitfall is to assume that a range like {1..200} can be expressed with variables using a=1;b=2;{$a..$b}.Due to what I described above,it simply is not possible,because it's the very first step in doing expansions.A possible way to achieve this,if you really can't handle this in another way,is using the eval command,which basically evaluates a commandline twice:

what is eval?

eval [arg ...]

              The  args  are read and concatenated together into a single com‐
              mand.  This command is then read and executed by the shell,  and
              its  exit status is returned as the value of eval.  If there are
              no args, or only null arguments, eval returns 0.

    the eval method requires that the entire command be properly escaped to avoid unexpected expansions.If the sequence expansion is to be assigned to an array,another method is using declaration commands:

declare -a 'pics=  (img{'"$a..$b"'}.png)'; mv "${
pics[@]}" ../imgs

What is declare?

declare: declare [-aAfFgilrtux] [-p] [name[=value] ...]

    Set variable values and attributes.
    
    Declare variables and give them attributes.  If no NAMEs are given,
    display the attributes and values of all variables.
    
    Options:
      -f    restrict action or display to function names and definitions
      -F    restrict display to function names only (plus line number and
        source file when debugging)
      -g    create global variables when used in a shell function; otherwise
        ignored
      -p    display the attributes and value of each NAME
    
    Options which set attributes:
      -a    to make NAMEs indexed arrays (if supported)
      -A    to make NAMEs associative arrays (if supported)
      -i    to make NAMEs have the `integer' attribute
      -l    to convert NAMEs to lower case on assignment
      -r    to make NAMEs readonly
      -t    to make NAMEs have the `trace' attribute
      -u    to convert NAMEs to upper case on assignment
      -x    to make NAMEs export
    
    Using `+' instead of `-' turns off the given attribute.
    
    Variables with the integer attribute have arithmetic evaluation (see
    the `let' command) performed when the variable is assigned a value.
    
    When used in a function, `declare' makes NAMEs local, as with the `local'
    command.  The `-g' option suppresses this behavior.
    
    Exit Status:
    Returns success unless an invalid option is supplied or an error occurs.

This method is significantly safer,but one must still be careful to control the values of $a and $b.

Both the exact quoting,and explicitly including "-a" are important.

The brace expansion is present in two basic forms,strings list and ranges

Strings list

{string1,string2,...,stringN}

Without the optional preamble and postscript strings,the result is just a space-separated list of the given strings:

$ echo {I,want,my,money,back}I want my money back

With preamble or postscript strings, the result is a space-separated list of all possible combinations of preamble, specified strings and postscript:

$ echo _{I,want,my,money,back}_I _want _my _money _back$ echo {I,want,my,money,back}_I_ want_ my_ money_ back_$ echo _{I,want,my,money,back}-_I- _want- _my- _money- _back-

The brace expansion is only performed,if the given string list is really a list of strings.i.e.if there's minimum one "," (comma)! Something like {money} doesn't expand to something special,it's really only the text "{money}".

Ranges

{
..
}

Brace expansion using ranges is written giving the startpoint and the endpoint of the range.This is a "sequence expression".The sequence can be of two types:

  • integers (optionally zero padded, optionally with a given increment)
  • characters
$ echo {5..12}5 6 7 8 9 10 11 12$ echo {c..k}c d e f g h i j k
When you mix these both types,brace expansion is not performed.
$ echo {5..k}{5..k}

When you zeropad one of the numbers(or both) in a range,then the generated range is zeropadded,too:

$ echo {01..10}01 02 03 04 05 06 07 08 09 10

Similar to the expansion using stringlists,you can add preamble and postscript strings:

$ echo 1.{0..9}1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9$ echo ---{A..E}------A--- ---B--- ---C--- ---D--- ---E---

Combining and nesting

When you combine more brace expansions,you effectively use a brace expansion as preamble or postscript for another one.Let's generate all possible combinations of uppercase letters and digits:

$ echo {A..Z}{0..9}A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 C0 C1 C2 C3 C4 C5 C6C7 C8 C9 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 F0 F1 F2 F3F4 F5 F6 F7 F8 F9 G0 G1 G2 G3 G4 G5 G6 G7 G8 G9 H0 H1 H2 H3 H4 H5 H6 H7 H8 H9 I0I1 I2 I3 I4 I5 I6 I7 I8 I9 J0 J1 J2 J3 J4 J5 J6 J7 J8 J9 K0 K1 K2 K3 K4 K5 K6 K7K8 K9 L0 L1 L2 L3 L4 L5 L6 L7 L8 L9 M0 M1 M2 M3 M4 M5 M6 M7 M8 M9 N0 N1 N2 N3 N4N5 N6 N7 N8 N9 O0 O1 O2 O3 O4 O5 O6 O7 O8 O9 P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 Q0 Q1Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q9 R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 S0 S1 S2 S3 S4 S5 S6 S7 S8S9 T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 U0 U1 U2 U3 U4 U5 U6 U7 U8 U9 V0 V1 V2 V3 V4 V5V6 V7 V8 V9 W0 W1 W2 W3 W4 W5 W6 W7 W8 W9 X0 X1 X2 X3 X4 X5 X6 X7 X8 X9 Y0 Y1 Y2Y3 Y4 Y5 Y6 Y7 Y8 Y9 Z0 Z1 Z2 Z3 Z4 Z5 Z6 Z7 Z8 Z9

Brace expansions can be nested,but too much of it usually makes you losing overview a bit.

Here's a sample to generate the alphabet,first the uppercase letters,then the lowercase ones:

$ echo {
{A..Z},{a..z}}A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z

Common use and examples

wget http://docs.example.com/documentation/slides_part{1,2,3,4,5,6}.htmlwget http://docs.example.com/documentation/slides_part{1..6}.htmlmkdir /home/bash/test/{foo,bar,baz,cat,dog}/home/bash/test/{foo,bar,baz,cat,dog}somecommand -v{,,,,}

/home/bash/test/{foo,bar,baz,cat,dog} generates /home/bash/test/foo /home/bash/test/bar /home/bash/test/baz /home/bash/test/cat /home/bash/test/dog

somecommand -v{,,,,} generates somecommand -v -v -v -v -v.

New in Bash 4.0

Zero padded number expansion

Prefix either of the numbers in a numeric range with 0 to pad the expanded numbers with the correct amount of zeros:

$ echo {0001..5}0001 0002 0003 0004 0005

Increment

It is now possible to specify an increment using ranges:
{
..
..
}
<INCR> is numeric,you can use a negative integer but the correct sign is deduced from the order of <START> and <END> anyways.
$ echo {1..10..2}1 3 5 7 9$ echo {10..1..2}10 8 6 4 2

Interesting feature:The increment specification also works for letter-ranges:

$ echo {a..z..3}a d g j m p s v y

What is Tilde expansion?

~~/...~NAME~NAME/...~+~+/...~-~-/...

The Tilde expansion is used to expand to several specific pathnames:

  • home directories
  • current working directory
  • previous working directory

Tilde expansion is only performed,when the tlide-construct is at the beginning of a word,or a separte word.

If there's nothing to expand,i.e. in case of a wrong username or any other error condition,the tlide construct is not replaced,it says what it is.

Tlide expansion is also performed everytime a variable is assigned:

  • after the
    first =:TARGET=~moonman/share
  • after
    every :(colon) in the assigned value:TARGET=file:~moonman/share

This way you can correctly use the the tlide expansino in your PATH:

PATH=~/mybins:~peter/mybins:$PATH
Spaces in the referenced pathes?A construct like...
~/"my directory"
…is perfectly valid and works!

Home directory

~~

This form expands to the home-directory of the current user(~) or the home directory of the given user(<NAME>).

If the given user doesn't exist(or if his home directory isn't determinable,for some reason),it doesn't expand to something else,it stays what it is.The requested home directory is found by asking the operating system for the associated home for <NAME>.

To find the home directory of the current user(~),Bash has a precedence:

  • expand to the value of HOME if it's defined
  • expand to the home directory of the user executing the shell(operating system)

That means,the variable HOME can override the "real" home directory,at least regarding tilde expansion.

Current working directory

~+
This expands to the value of the PWD variable,which holds the correct working directory.
echo "CWD is $PWD"
is equivalent to (note is must be a separate word!)
echo "CWD is" ~+

Previous working directory

~-
This expands to the value of the OLDPWD variable,which holds the previous working directory(the one before the last cd).if OLDPWD is unset(never changed the directory ),it is not expanded.
$ pwd/home/bash$ cd /etc$ echo ~-/home/bash

What is parameter expansion?

one core functionality of Bash is to manage parameters.A parameter is an entity that stores values and is referenced  by a name,a number or a special symbol.

  • Parameters referenced by a name are called variables.(this also applies to arrays)
  • Parameters referenced by a number are called positional parameters and reflect the arguments given to a shell.
  • Parameters referenced by a special symbol are auto-set parameters that have different special meanings and uses.

Parameter expansion is the procedure to get the value from the referenced entity,like expanding a variable to print its value.On expansion time you can do very nasty things with the parameter or its value.These things are described here.

If you saw some parameter expansion syntax somewhere,and you need to check what it can be,try the overview section below!

Arrays can be special cases for parameter expansion,every applicable description mentions arrays below.

Simple usage

  • $PARAMETER
  • ${PARAMETER}

The easiest form is to just use a parameter's name within braces.This is identical to using $Foo like you see it everywhere,but has the advantage that it can be immediately followed by characters that would be interpreted as part of the parameter name otherwise.Compare these two expressions,where we want to print a word with a trailing 's'.

echo "The plural of $WORD is most likely $WORDs"echo "The plural of $WORD is most likely ${WORD}s"

Why does the first one fail?It prints nothing,because a parameter(variable) named "WORDs" is undefined and thus printed as ""(nothing).Without using braces for parameter expansion,Bash will interpret the sequence of all valid characters from the introducing "$" up to the last valid character as name of the parameter.When using braces you just force bash to only interpret the name inside your braces.

Also,please remember,that parameter names are (like nearly everything in UNIX@) case sentitive!

The second form with the curly braces is also needed to access positional parameters(arguments to script) beyond $9:

echo "Argument  1 is: $1"echo "Argument 10 is: ${10}"

Arrays:

For arrays you always need the braces.The arrays are expanded by individual indexes or mass arguments.An individual index behaves like a normal parameter.

Purpose

An array is a parameter that holds mappings from keys to values.Arrays are used to store a collection of parameters into a parameter.Arrays(in any programming language) are a useful and common composite data structure,and one of the most important scripting features in Bash and other shells.

Here is an abstract representation of an array named NAMES.The indexes go from 0 to 3.

NAMES 0: Peter 1: Anna 2: Greg 3: Jan
Instead of using 4 separate variables,multiple related variables are grouped grouped together into elements of the array,accessible by their key.If you want the second name,ask for index 1 of the array NAMES.

转载于:https://my.oschina.net/huihu/blog/181541

你可能感兴趣的文章
BigPipe(FaceBook使用的页面加载技术) JAVA实现代码示例
查看>>
js提示框
查看>>
为什么关系型数据库不适于Hadoop
查看>>
Android app如何正确读写系统sys设备节点
查看>>
Redis常用命令
查看>>
换种角度解决问题
查看>>
tensorflow框架---前向传播
查看>>
Nginx Http认证 实现访问网站或目录密码认证保护 | 使用 HttpAuthBasicModule 模块
查看>>
MySQL 5.6 Generic Binary安装与配置
查看>>
Oracle创建临时表空间,数据表空间,用户,给用户授权
查看>>
SQL数据库语言总结及代码示例
查看>>
PHP开发APP接口
查看>>
功能测试报告
查看>>
获取手机联系人信息
查看>>
关于64位操作系统,应用程序占用内存飙升的问题解决方法记录
查看>>
Quartz.NET 调度配置说明
查看>>
统计Redis中各种数据大小
查看>>
JFinal-Beetl-Shiro(JdbcRealm)-例子
查看>>
java中的内部类总结
查看>>
跬步之积,以至千里
查看>>