Zsh Development Guide (Chapter 19 script example explanation)

Time:2021-10-27

Reading guide

This article will explain some simple Zsh script examples.

Example 1: copy the directory structure of a directory

Function:

Copy a directory and all directories under it to another directory (that is, create a directory with the same name), but do not copy other types of files under the directory.

example:

Directory structure of SRC:

src
├── a
├── b
│   ├── 1.txt
│   └── 2
│       └── 3.txt
├── c.txt
├── d
├── e f
│   └── g
│       └── 4.txt
└── g h -> e f

To construct a DST directory, only the directory under SRC is included, as follows:

dst
└── src
    ├── a
    ├── b
    │   └── 2
    ├── d
    └── e f
        └── g

Idea:

  1. First, you need to filter out the directory names under the SRC directory. You can use**/*(/)Match.
  2. Then usemkdir -pCreate the corresponding directory in the DST directory.
#Parameter 1: SRC directory
#Parameter 2: DST directory to be created

#!/bin/zsh

for i ($1/**/*(/)) {
    #The - P parameter creates directories recursively, regardless of the order in which they are created
    mkdir -p $2/$i
}

Example 2: finding unmatched files

Function:

You need some. Txt and. Txt.md5sum files in the current directory. You need to find the. TXT file without the corresponding. Md5sum file. (the actual scenario is to find the files that have been downloaded, and the files that have not been downloaded correspond to a file with suffix.)

example:

All files in the current directory:

aa.txt
bb.txt
bb.txt.md5sum
cc dd.txt
cc dd.txt.md5sum
ee ff.txt.md5sum
gg.txt
hh ii.txt

You need to find the. TXT file that does not correspond to. Md5sum:
aa.txt
gg.txt
hh ii.txt

Idea:

  1. Find all. Md5sum files, and then remove. Md5sum from the file name, that is, those. Txt files that need to be excluded (a).
  2. For all files, exclude the. M5sum file, and then exclude a, that is, the result.

realization:

#!/bin/zsh

all_files=(*)
bad_files=(*.md5sum)
bad_files+=(${bad_files/.md5sum})

#Array subtraction operation
echo ${all_files:|bad_files}

Example 3: batch renaming files with sed

Function:

Rename files in batches in the form of the SED command.

example:

#Implement the renamex command. The first parameter accepted is the body parameter of SED, and the other parameters are the file list
#The effect is to rename these files according to sed's changes to the file names

% tree
.
├── aaa_aaa.txt
├── aaa.txt
├── ccc.txt
└── xxx
    ├── aaa bbb.txt
    └── bbb ccc.txt

% renamex s/aaa/bbb/g **/*
'aaa_aaa.txt' -> 'bbb_bbb.txt'
'aaa.txt' -> 'bbb.txt'
'xxx/aaa bbb.txt' -> 'xxx/bbb bbb.txt'

% tree
.
├── bbb_bbb.txt
├── bbb.txt
├── ccc.txt
└── xxx
    ├── bbb bbb.txt
    └── bbb ccc.txt

Idea:

  1. To find all the file names, replace them with sed.
  2. If the file name changes, use the MV command to move

realization:

#!/bin/zsh

(($+2)) || {
    echo 'Usage: renamex s/aaa/bbb/g *.txt'
    return
}

for name ($*[2,-1]) {
    local new_name="$(echo $name | sed $1)"
    [[ $name == $new_name ]] && continue
    mv -v $name $new_name
}

Example 4: delete duplicate files according to MD5 of files

Function:

Delete all duplicate files in the current directory and subdirectories (not very rigorous according to MD5 judgment).

Idea:

  1. Use the md5sum command to calculate MD5 for all files.
  2. Use the hash table to determine whether MD5 is duplicated, and delete the subsequent files with MD5 in the hash table.

realization:

#!/bin/zsh

#D is a hidden file that contains files that begin with
local files=("${(f)$(md5sum **/*(.D))}")
local files_to_delete=()
local -A md5s

for i ($files) {
    #Take the first 32 bits, that is, the length of MD5
    local md5=$i[1,32]

    if (($+md5s[$md5])) {
        #Take the content after 35 bits, that is, the file path. There are two spaces after MD5
        files_to_delete+=($i[35,-1])
    } else {
        md5s[$md5]=1
    }
}

(($#files_to_delete)) && rm -v $files_to_delete

Example 5: convert Chinese numerals within 100 to Arabic numerals

Function:

Convert Chinese numerals within 100 to Arabic numerals, such as 68 to 68.

Idea:

  1. Build a hash table to store the corresponding relationship between Chinese characters and numbers.
  2. The trouble is “ten”. In different positions, the numbers converted are different and need to be handled separately.

realization:

#!/bin/zsh

local -A table=(
Zero 0
One one
II. 2
Three three
IV 4
Five five
VI 6
VII. 7
VIII. 8
IX 9
)

local result

If [[$1 = = ten]]{
    Result = zero
}Elif [[$1 = = ten *]]{
    Result = ${1 / ten / one}
}Elif [[$1 = = * ten]]{
    Result = ${1 / ten / zero}
}Elif [[$1 = = * ten *]]{
    Result = ${1 / ten}
} else {
    result=$1
}

for i ({1..$#result}) {
    result[i]=$table[$result[i]]

    if [[ -z $result[i] ]] {
        echo error
        return 1
    }
}

echo $result

Operation results:

%. / convert I
1
%. / convert x
10
%. / convert x五
15
%. / convert twenty
20
%. / convert 56
56
%. / convert I百
error

Example 6: rename the file name with Chinese characters and numbers to start with the corresponding number

Function:

See the following example.

example:

The current directory has the following files:

Zsh Development Guide (Part I - variables and statements). MD
Zsh Development Guide (Part VII - numerical calculation). MD
Zsh Development Guide (Part 3 - escape characters and formatted output of string processing). MD
Zsh Development Guide (Part 9 - functions and scripts). MD
Zsh Development Guide (Part 2 - common operations in string processing). MD
Zsh Development Guide (Part 5 - array). MD
Zsh Development Guide (Part 8 - variable modifiers). MD
Zsh Development Guide (Part 6 - hash table). MD
Zsh Development Guide (Chapter 11 - advanced content of variables). MD
Zsh Development Guide (Part 17 - use socket file and TCP to realize interprocess communication). MD
Zsh Development Guide (Part 13 - pipeline and redirection). MD
Zsh - Development Guide (Part 19 - explanation of script examples). MD
Zsh Development Guide (usage of Chapter 12 - [[-]]). MD
Zsh Development Guide (Part 15 - process and job control). MD
Zsh Development Guide (Chapter 18 - usage of more built-in modules). MD
Zsh Development Guide (Part 16 - usage of alias - and eval -). MD
Zsh Development Guide (Part 14 - document reading and writing). MD
Zsh Development Guide (Part 10 - file search and batch processing). MD
Zsh Development Guide (Part 4 - wildcards in string processing). MD

It needs to be renamed as follows:

01_Zsh Development Guide (Part I - variables and statements). MD
02_Zsh Development Guide (Part 2 - common operations in string processing). MD
03_Zsh Development Guide (Part 3 - escape characters and formatted output of string processing). MD
04_Zsh Development Guide (Part 4 - wildcards in string processing). MD
05_Zsh Development Guide (Part 5 - array). MD
06_Zsh Development Guide (Part 6 - hash table). MD
07_Zsh Development Guide (Part VII - numerical calculation). MD
08_Zsh Development Guide (Part 8 - variable modifiers). MD
09_Zsh Development Guide (Part 9 - functions and scripts). MD
10_Zsh Development Guide (Part 10 - file search and batch processing). MD
11_Zsh Development Guide (Chapter 11 - advanced content of variables). MD
12_Zsh Development Guide (usage of Chapter 12 - [[-]]). MD
13_Zsh Development Guide (Part 13 - pipeline and redirection). MD
14_Zsh Development Guide (Part 14 - document reading and writing). MD
15_Zsh Development Guide (Part 15 - process and job control). MD
16_Zsh Development Guide (Part 16 - usage of alias - and eval -). MD
17_Zsh Development Guide (Part 17 - use socket file and TCP to realize interprocess communication). MD
18_Zsh Development Guide (Chapter 18 - usage of more built-in modules). MD
19_Zsh - Development Guide (Part 19 - explanation of script examples). MD

Idea:

  1. First, you need to write a function to convert Chinese characters into Arabic numerals.
  2. Then you need to intercept Chinese numerals from the file name and convert them into Arabic numerals.
  3. Splice the file name and move the file.

realization:

#!/bin/zsh

#The logic for converting numbers is the same as in the previous example

local -A table=(
Zero 0
One one
II. 2
Three three
IV 4
Five five
VI 6
VII. 7
VIII. 8
IX 9
)

convert() {
    local result

    If [[$1 = = ten]]{
        Result = zero
    }Elif [[$1 = = ten *]]{
        Result = ${1 / ten / one}
    }Elif [[$1 = = * ten]]{
        Result = ${1 / ten / zero}
    }Elif [[$1 = = * ten *]]{
        Result = ${1 / ten}
    } else {
        result=$1
    }

    for i ({1..$#result}) {
        result[i]=$table[$result[i]]

        if [[ -z $result[i] ]] {
            echo error
            return 1
        }
    }

    echo $result
}

for i (Zsh*.md) {
    #- Z 2 is to complete a 0 in the front
    #Remove all before "chapter" and after "chapter" of the file name
    Local - Z 2 num = $(convert ${${i#* part}% *})
    mv -v $i ${num}_$i
}

Example 7: unified compression and decompression tool

Function:

There are many common compression and archiving formats and different parameters under Linux. Write a compression and decompression tool with unified usage for creation and decompression.zip .7z .tar .tgz .tbz2 .txz .tar.gz .tar.bz2 .tar.xz .cpio .ar .gz .bz2 .xzAnd other documents. (similar)atool, butatoolI haven’t updated it for a long time. Some new formats don’t support it and can’t be customized. And withperlIt’s written. It’s hard to understand. So I decided to write one myself and only cover itatoolSome common functions of.)

example:

#A is used to create compressed files
% a a.tgz dir1 file1 file2
dir1/
file1
file2

#Al is used to list the files in the compressed file
% al a.tgz
drwxr-xr-x goreliu/goreliu   0 2017-09-13 11:23 dir1/
-rw-r--r-- goreliu/goreliu   3 2017-09-13 11:23 file1
-rw-r--r-- goreliu/goreliu   3 2017-09-13 11:23 file2

#X is used to extract files
% x a.tgz
dir1/
file1
file2
a.tgz  ->  a

#If the extracted file name or directory name already exists under the current directory, extract it to the random directory
% x a.tgz
dir1/
file1
file2
a.tgz  ->  /tmp/test/x-c4I

Idea:

  1. When compressing a file, judge the format of the compressed file according to the incoming file name.
  2. When decompressing and viewing the contents of compressed files, according to the passed in file name andfileThe command result determines the format of the compressed file.
  3. In order to reuse the code, multiple commands are integrated into a file, and thenln -sInto multiple commands.

realization:

#!/bin/zsh

get_type_by_name() {
    case $1 {
        (*.zip|*.7z|*.jar)
        echo 7z
        ;;

        (*.rar|*.iso)
        echo 7z_r
        ;;

        (*.tar|*.tgz|*.txz|*.tbz2|*.tar.*)
        echo tar
        ;;

        (*.cpio)
        echo cpio
        ;;

        (*.cpio.*)
        echo cpio_r
        ;;

        (*.gz)
        echo gz
        ;;

        (*.xz)
        echo xz
        ;;

        (*.bz2)
        echo bz2
        ;;

        (*.lzma)
        echo lzma
        ;;

        (*.lz4)
        echo lz4
        ;;

        (*.ar)
        echo ar
        ;;

        (*)
        return 1
        ;;
    }
}

get_type_by_file() {
    case $(file -bz $1) {
        (Zip *|7-zip *)
        echo 7z
        ;;

        (RAR *)
        echo 7z_r
        ;;

        (POSIX tar *|tar archive)
        echo tar
        ;;

        (*cpio archive*)
        echo cpio
        ;;

        (*gzip *)
        echo gz
        ;;

        (*XZ *)
        echo xz
        ;;

        (*bzip2 *)
        echo bz2
        ;;

        (*LZMA *)
        echo lzma
        ;;

        (*LZ4 *)
        echo lz4
        ;;

        (current ar archive)
        echo ar
        ;;

        (*)
        return 1
        ;;
    }
}


(($+commands[tar])) || alias tar=bsdtar
(($+commands[cpio])) || alias cpio=bsdcpio

case ${0:t} {
    (a)

    (($#* >= 2)) || {
        echo Usage: $0 target files/dirs
        return 1
    }

    case $(get_type_by_name $1) {
        (7z)
        7z a $1 $*[2,-1]
        ;;

        (tar)
        tar -cavf $1 $*[2,-1]
        ;;

        (cpio)
        find $*[2,-1] -print0 | cpio -H newc -0ov > $1
        ;;

        (gz)
        gzip -cv $*[2,-1] > $1
        ;;

        (xz)
        xz -cv $*[2,-1] > $1
        ;;

        (bz2)
        bzip2 -cv $*[2,-1] > $1
        ;;

        (lzma)
        lzma -cv $*[2,-1] > $1
        ;;

        (lz4)
        lz4 -cv $2 > $1
        ;;

        (ar)
        ar rv $1 $*[2,-1]
        ;;

        (*)
        echo $1: error
        return 1
        ;;
    }
    ;;

    (al)

    (($#* >= 1)) || {
        echo Usage: $0 files
        return 1
    }

    for i ($*) {
        case $(get_type_by_name $i || get_type_by_file $i) {
            (7z|7z_r)
            7z l $i
            ;;

            (tar)
            tar -tavf $i
            ;;

            (cpio|cpio_r)
            cpio -itv < $i
            ;;

            (gz)
            zcat $i
            ;;

            (xz)
            xzcat $i
            ;;

            (bz2)
            bzcat $i
            ;;

            (lzma)
            lzcat $i
            ;;

            (lz4)
            lz4cat $i
            ;;

            (ar)
            ar tv $i
            ;;

            (*)
            echo $i: error
            ;;
        }
    }
    ;;

    (x)

    (($#* >= 1)) || {
        echo Usage: $0 files
        return 1
    }

    for i ($*) {
        local outdir=${i%.*}

        [[ $outdir == *.tar ]] && {
            outdir=$outdir[1, -5]
        }

        if [[ -e $outdir ]] {
            outdir="$(mktemp -d -p $PWD x-XXX)"
        } else {
            mkdir $outdir
        }

        case $(get_type_by_name $i || get_type_by_file $i) {
            (7z|7z_r)
            7z x $i -o$outdir
            ;;

            (tar)
            tar -xavf $i -C $outdir
            ;;

            (cpio|cpio_r)
            local file_path=$i
            [[ $i != /* ]] && file_path=$PWD/$i
            cd $outdir && cpio -iv < $file_path && cd ..
            ;;

            (gz)
            zcat $i > $outdir/$i[1,-4]
            ;;

            (xz)
            xzcat $i > $outdir/$i[1,-4]
            ;;

            (bz2)
            bzcat $i > $outdir/$i[1,-5]
            ;;

            (lzma)
            lzcat $i > $outdir/$i[1,-6]
            ;;

            (lz4)
            lz4cat $i > $outdir/$i[1,-5]
            ;;

            (ar)
            local file_path=$i
            [[ $i != /* ]] && file_path=$PWD/$i
            cd $outdir && ar x $file_path && cd ..
            ;;

            (*)
            echo $i: error
            ;;
        }

        local files=$(ls -A $outdir)

        if [[ -z $files ]] {
            rmdir $outdir
        } elif [[ -e $outdir/$files && ! -e $files ]] {
            mv -v $outdir/$files . && rmdir $outdir
            echo $i " -> " $files
        } else {
            echo $i " -> " $outdir
        }
    }
    ;;

    (*)
    echo error
    return 1
    ;;
}

Example 8: a tool to run commands concurrently

Function:

We often encounter the scene of batch processing of files in the loop (for example, converting all JPG images into PNG images), so we will encounter a trouble: if we process files in the foreground, we can only process one file at a time, which is too inefficient; If files are processed in the background, many processes will be started in an instant, occupying a lot of resources, which is difficult for the system to bear. What we want is to process a fixed number (such as 10) of files at the same time. If this number has been reached, wait for a while until there is an exit process.parallelThe command can meet this demand to a certain extent, but it is too cumbersome to use.

example:

#RR is a function (can be placed in. Zshrc), which can be used directly by adding RR command
#The command supports variables, redirection, etc., which is no different from the direct input command in format (alias is not supported)
% rr sleep 5
[4] 5031
% rr sleep 5
[5] 5032

#If no parameters are added, the number of currently running processes, the maximum number of concurrent processes and the process number of running processes are displayed
#The default maximum number of concurrent processes is 10
% rr
running/max: 2/10
pid: 5031 5032
#After 5 seconds, the operation ends
% rr
running/max: 0/10


#Use - J to specify the maximum number of concurrent processes. You can specify it once. If you need to modify it, you can specify it again
#You can only adjust the maximum number of concurrent processes without running commands
% rr -j2 sleep 10
[4] 5035
% rr sleep 10
[5] 5036

#The maximum number of concurrent processes is exceeded. Wait and check whether any process exits every second
#If a process exits, the current command continues to run in the background
% rr sleep 10
running/max: 2/2, wait 1s ...
pid: 5035 5036
running/max: 2/2, wait 1s ...
pid: 5035 5036
[4]  - done       $*
[4] 5039


#In the actual use scenario, convert JPG pictures into PNG pictures in batches. GM is the command in graphicsmagick
#Converting the image format is time-consuming, and it takes a long time to execute in sequence
% for i (*.jpg) { rr gm convert $i ${i/jpg/png} }
[4] 5055
[5] 5056
[6] 5057
[7] 5058
[8] 5059
[9] 5060
[10] 5061
[11] 5062
[12] 5063
[13] 5064
running/max: 10/10, wait 1s ...
pid: 5060 5061 5062 5063 5064 5055 5056 5057 5058 5059
running/max: 10/10, wait 1s ...
pid: 5060 5061 5062 5063 5064 5055 5056 5057 5058 5059
[11]    done       $*
[5]    done       $*
[5] 5067
[12]    done       $*
[11] 5068
[6]    done       $*
[6] 5069
[12] 5070
running/max: 10/10, wait 1s ...
pid: 5070 5060 5061 5064 5055 5067 5068 5069 5058 5059
[13]  - done       $*
[4]    done       $*
[4] 5072
[13] 5073
running/max: 10/10, wait 1s ...
pid: 5070 5060 5072 5061 5073 5067 5068 5069 5058 5059
[5]    done       $*
[6]    done       $*
[5] 5075
[6] 5076
running/max: 10/10, wait 1s ...
pid: 5070 5060 5072 5061 5073 5075 5076 5068 5058 5059
...

Idea:

  1. The maximum number of concurrent processes and the currently running processes (hash table) need to be recorded in the global variable.
  2. For each running process, put the corresponding process number into the hash table.
  3. If the current number of running processes reaches the maximum number of concurrent processes, the loop checks whether the process in the hash table exits.

realization:

rr() {
    (($+max_process)) || typeset -g max_process=10
    (($+running_process)) || typeset -gA running_process=()

    [[ $1 == -j<1-> ]] && {
        max_process=${1[3,-1]}
        shift
    }

    (($# == 0)) && {
        for i (${(k)running_process}) {
            [[ -e /proc/$i ]] || unset "running_process[$i]"
        }

        echo "running/max: $#running_process/$max_process"
        (($#running_process > 0)) && echo "pid: ${(k)running_process}"
        return
    }

    while ((1)) {
        local running_process_num=$#running_process

        if (($running_process_num < max_process)) {
            $* &
            running_process[$!]=1
            return
        }

        for i (${(k)running_process}) {
            [[ -e /proc/$i ]] || unset "running_process[$i]"
        }

        (($#running_process == $running_process_num)) && {
            echo "running/max: $running_process_num/$max_process, wait 1s ..."
            echo "pid: ${(k)running_process}"
            sleep 1
        }
    }
}

Example 9: batch conversion of picture format

Function:

Convert all common picture formats of the current directory and subdirectories into JPG format (jpg format should also be converted once to reduce the file volume), and then delete the original picture. It needs to be handled by five concurrent processes. Be careful to avoid overwriting files with different extensions.

example:

% tree
.
├── mine
│     Text - letter.txt
│     Note - first letter.jpg
│     └ - second letter.jpg
- funny
│     - sell cute.gif
│     Cats eat fish.gif
│     └ - Cat grabs food.gif
└ - material
    Key - 104 key template.jpg
    Key - 104 key template.psd
    ├── ahk
    │   ├── ahk_bg.jpg
    │   ├── ahk_home_logo.jpg
    │   ├── ahk_home_logo.txt
    │   ├── ahk_home_qr.jpg
    │   ├── ahk_home_qr_small.jpg
    │   └── ahk_logo.png
    ├── stp_fc_cw_png_pk
    │   ├── HD.PNG
    │   ├── newimage.png
    │   ├── nshd.PNG
    │   └── std.png
    Image - earth.jpg
    Galaxy.jpeg
    - wood grain background.gif
    Image - wood grain background.jpeg
    └ - wood grain background.jpg

5 directories, 23 files

% alltojpg
running/max: 0/5
running: 5, wait 1.0000000000s ...
pid: 5953 5954 5955 5956 5957
running: 5, wait 1.0000000000s ...
pid: 5965 5966 5967 5968 5959

% tree
.
├── mine
│     Text - letter.txt
│     Note - first letter.jpg
│     └ - second letter.jpg
- funny
│     - sell cute_ g.jpg
│     Cats eat fish_ g.jpg
│     └ - Cat grabs food_ g.jpg
└ - material
    Key - 104 key template.jpg
    Key - 104 key template.psd
    ├── ahk
    │   ├── ahk_bg.jpg
    │   ├── ahk_home_logo.jpg
    │   ├── ahk_home_logo.txt
    │   ├── ahk_home_qr.jpg
    │   ├── ahk_home_qr_small.jpg
    │   └── ahk_logo_p.jpg
    ├── stp_fc_cw_png_pk
    │   ├── HD_p.jpg
    │   ├── newimage_p.jpg
    │   ├── nshd_p.jpg
    │   └── std_p.jpg
    Image - earth.jpg
    Galaxy - Galaxy_ e.jpg
    - wood grain background_ e.jpg
    - wood grain background_ g.jpg
    └ - wood grain background.jpg

5 directories, 23 files

Idea:

  1. See the previous example for the method of running commands concurrently.
  2. For converting picture formatsgm convertCommand (in graphicsmagick) orconvertCommand (in ImageMagick).
  3. Common picture file extensions arejpg jpeg png gifIn addition, it may be an uppercase extension.
  4. To avoid similara.gifcovera.jpgAdd different suffixes for different file formats, so that there is no need to check whether there is a file with the same name to speed up the speed.

realization:

#!/bin/zsh

#RR is an improved version of the code in the previous example
rr() {
    (($+max_process)) || typeset -gi max_process=10
    (($+check_interval)) || typeset -gF check_interval=1
    (($+running_process)) || typeset -gA running_process=()

    while {getopts i:j:h arg} {
        case $arg {
            (i)
            ((OPTARG > 0)) && check_interval=$OPTARG
            ;;

            (j)
            ((OPTARG > 0)) && max_process=$OPTARG
            ;;

            (h)
            echo "Usage: $0 [-i check_interval] [-j max_process] [cmd] [args]"
            return
            ;;
        }
    }

    shift $((OPTIND - 1))

    (($# == 0)) && {
        for i (${(k)running_process}) {
            [[ -e /proc/$i ]] || unset "running_process[$i]"
        }

        echo "running/max: $#running_process/$max_process"
        (($#running_process > 0)) && echo "pid: ${(k)running_process}"
        return 0
    }

    while ((1)) {
        local running_process_num=$#running_process

        if (($running_process_num < max_process)) {
            $* &
            running_process[$!]=1
            return
        }

        for i (${(k)running_process}) {
            [[ -e /proc/$i ]] || unset "running_process[$i]"
        }

        (($#running_process == $running_process_num)) && {
            echo "running: $running_process_num, wait ${check_interval}s ..."
            echo "pid: ${(k)running_process}"
            sleep $check_interval
        }
    }
}


#Jpg as intermediate extension
rename .JPG .jpg **/*.JPG

#Set the number of concurrent processes to 5
rr -j5

for i (**/*.(jpg|png|PNG|jpeg|JPEG|gif|GIF)) {
    rr gm convert $i $i.JPG
}

#Wait until all operations are completed
wait

#Delete original file
rm **/*.(jpg|png|PNG|jpeg|JPEG|gif|GIF)

#Avoid overwriting files with the same name
rename .jpg.JPG .jpg **/*.JPG
rename .png.JPG _p.jpg **/*.JPG
rename .PNG.JPG _p.jpg **/*.JPG
rename .jpeg.JPG _e.jpg **/*.JPG
rename .JPEG.JPG _e.jpg **/*.JPG
rename .gif.JPG _g.jpg **/*.JPG
rename .GIF.JPG _g.jpg **/*.JPG

summary

This article explains several practical Zsh scripts, and more may be added later.

Update history

September 13, 2017: “example 7”, “example 8” and “example 9” were added.

This article will not be updated. The full series of articles will be updated and maintained here:github.com/goreliu/zshguide

Pay to solve problems related to windows, Linux, shell, C, C + +, AHK, python, JavaScript, Lua and other fields, flexible pricing, welcome to consult, wechat ly50247.