photon
10/1/2018 - 11:02 AM

Rename pairs of link files in the shell. Rename by multiple pairs of link file names.

Rename pairs of link files in the shell. Rename by multiple pairs of link file names.

#!/bin/bash

# The bubbles in the asset markets 
#     are equivalent to various taxes,
#     payed by each of us in the long term,
#     whether a winner or loser in the game.

# Features:
# Change a pair of link files 
#     from
#         link1.old -> target1.old
#     to
#         link1.new -> target1.new
# Rename by multiple pairs of link file names
# A lightweight alternative to other heavy tools.

# Dependency:
# The `realpath` package is required
# Use the commands of `mv`,`ln`,`rm` etc. underlyingly.

# Usage:
# Link and target files should have the same new name, 
#     but not necessarily the same old name.
# 1. Rename link/target by one pair of file names:
#        ./rename_linkets.bash link_old_path new_name
#    The link_old_path could be in the format of:
#        ./old_link.yy or old_link.yy
#        ./old_link or old_link
#        ~/old_link
#        /path/to/old_link
#    The new_name should be the file/dir name that would be changed to
#        and shouldn't include the upper level parent path

# 2. Rename files by multiple pairs of file names:
#        ./rename_linkets.bash "old_link1 old_link2 .." "new_name1 new_name2 .."
#    The number of old_link files should be the same as new names:
#        ./rename_linkets.bash "./{link1,link2}" "new_name1 new_name2"
#        ./rename_linkets.bash "./{link1,link2} c1" "new_name1 new_name2 c2"

# Linket: 
# A pair of files for linking, that work in the form of:
#     Link file -> Target file

# #!/bin/bash
# 
# # Test the usage of rename_linkets.bash
# # Operation on some files would fail on purpose 
# #     to show failure cases 
# 
# # use ./test_rename_linkets.bash -o 
# #     to override the check on the test dir/files
# 
# echo_start(){
#     echo "Start test $1 ============================="
# }
# 
# init(){
#     echo_start "$1"
#     rm -r ./test9
#     mkdir -p ./test9/dir1 ./test9/dir2
# }
# 
# run_tests(){     
#     if [[ -e ./test9 && "-o" != "$option" ]] 
#         then
#         echo "Dir test9 existed. Quit."
#         exit 1 
#     fi
# 
#     for test in "$@"
#         do
#         init "$test"
#         $test
#     done
# }
# 
# test1(){
#     touch ./test9/b.o
#     touch ./test9/dir2/r.o
#     
#     ln -s ../dir2/t.o ./test9/dir1/t.o
#     ln -s "../dir2/r.o" ./test9/dir1/r.o
# 
#     ./rename_linkets.bash "./test9/a.o test9/b.o ./test9/dir1/{t.o,r.o}" "a.n b.n t.n r.n"
# }
# 
# test2(){
#     if [ "-o" != "$option" ] 
#         then
#         for f in `echo "u.o u.n"`
#             do
#             echo "File $f existed. Quit."
#             exit 1 
#         done
#     fi
# 
#     rm u.o u.n ./test9/u.o
#     touch ./test9/u.o
#     ln -s ./test9/u.o u.o 
#     ./rename_linkets.bash u.o u.n
# }
# 
# test3(){
#     # test dangerous path 
#     touch ./test9/dir2/dg.o
#     ln -s ../dir2/dg.o ./test9/dir1/dg.o
#     ./rename_linkets.bash ./test9/dir1/dg.o dg.n
# }
# 
# option="$1"
# 
# #run_tests test1
# #run_tests test2
# #run_tests test3
# run_tests test1 test2
# 

echo "Start script ----------------------"

# f1: old link file or old target file 
#     of the linket (link pair)
# f2: new link file or new target file
#     of the linket (link pair)
# Linket: a pair of files for linking, that work in the form of:
#     Link file -> Target file

# Raw path format: ~/dir{1,2}/
# expanded path format: /home/user/dir1/ /home/user/dir2/
# fine path format: /home/user/dir1 /home/user/dir2

check_dependency(){
    rslt=$(realpath --version)
    bin_name="${rslt:0:8}"
    if [ "realpath" != "$bin_name" ]
        then
        echo "ERR:: Require the package of realpath, but realpath is not found."
        echo "      To install realpath, run the command of:"
        echo "      sudo apt-get update && sudo apt-get install realpath"
        echo "      Exit."
        exit 1
    fi
}

expand_path(){
    # raw path format: ~/s1 /y/tt1/ t1 ~/{s1,s2}
    str_expanded_paths_of_f1="$(eval echo $str_raw_paths_of_f1)"
    #echo "str_expanded_paths_of_f1:" $str_expanded_paths_of_f1
}

check_nums_of_path_and_name(){
    arr_expanded_paths_of_f1=($str_expanded_paths_of_f1)
    arr_names_of_f2=($str_names_of_f2)
    #echo "arr_expanded_paths_of_f1: ${arr_expanded_paths_of_f1[@]}"
    
    if [ ${#arr_expanded_paths_of_f1[@]} -ne ${#arr_names_of_f2[@]} ] 
        then
        echo "ERR:: The number of old files and the number of new names" 
        echo "ERR::      are not matched."
        exit 1
    fi
}

check_arg_nums(){
    length_arglist_raw=$#
    
    if [ 2 -gt $length_arglist_raw ]
        then
        echo "ERR:: Lacking operand for $0"
        exit 1
    fi
    
    check_nums_of_path_and_name
}

check_if_link_file_exist(){
    expanded_f1_path="$1"
    
    # tricky test conditions here.
    if [ -h "$expanded_f1_path" ] 
        then
        # -a not work here, use -e
        if [ ! -e "$expanded_f1_path" ] 
            then
            target_path=$(readlink -m "$expanded_f1_path")
            echo "NOTICE:: Broken link: $expanded_f1_path -> $target_path"
            return 1
        fi
        return 0
    fi
    
    if [ ! -f "$expanded_f1_path" ] 
        then
        echo "NOTICE:: Couldn' find linket link file for path: $expanded_f1_path"
        return 1
    fi
    
    if [ -f "$expanded_f1_path" ] 
        then
        echo "NOTICE:: Not a linket link file, but a regular file: $expanded_f1_path"
        return 1
    fi
}

check_if_is_dangerous_path(){
    fpath="$1"
    ftype="$2"
    
    # 1 means "no danger", 0 means "has danger"
    path_has_danger=1
    
    dangerous_path_list=("/")
    #dangerous_path_list=("~/data_mnt/_____data/linux/command_tool/mv/rename_linkets/test9/dir1/dg.o")
    for dangerous_path in "${dangerous_path_list[@]}"
    do
        if [ "$dangerous_path" == "$fpath" ]
            then
            path_has_danger=0
            info="$2 old path has dangerous path "$dangerous_path" in : $fpath"
            echo "ERR:: $info. Exit."
            exit 1
        fi
    done
}

make_f2path_by_f1path_f2name(){
    # make f2path for either link or target
    # by their f1path and new name
    f1_path=$1
    f2_name=$2

    f1_parent_path=${f1_path%/*}
    f2_path=$f1_parent_path/$f2_name
}

make_paths_of_linket_link(){
    make_f2path_by_f1path_f2name "$f1_path_of_link" "$f2_name"
    f2_path_of_link=$f2_path

    ftype="Linket-link"
    check_if_is_dangerous_path "$f1_path_of_link" "$ftype"
}

make_paths_of_linket_target(){
    f1_path_of_target=$(realpath "$f1_path_of_link")   
    make_f2path_by_f1path_f2name "$f1_path_of_target" "$f2_name"
    f2_path_of_target=$f2_path
    
    ftype="Linket-target"
    check_if_is_dangerous_path "$f2_path_of_target" "$ftype"
}

make_paths_of_linket(){   
    # Linket: a pair of files for linking, that work in the form of:
    #     Link file -> Target file
    f1_path_of_link=$(realpath -s "$1")
    f2_name=$2
    
    make_paths_of_linket_link
    make_paths_of_linket_target
}

make_paths_of_linket_in_oneloop(){
    make_paths_of_linket "$expanded_f1_path" "$f2_name"    

    # keey the arr for future debug info
    arr_f1_paths_of_link[$i]=$f1_path_of_link
    arr_f2_paths_of_link[$i]=$f2_path_of_link
    arr_f1_paths_of_target[$i]=$f1_path_of_target
    arr_f2_paths_of_target[$i]=$f2_path_of_target
}

mv_target_file(){
    cmd="mv $f1_path_of_target $f2_path_of_target"
}

ln_new_target_file(){
    cmd="ln -s $f2_path_of_target $f2_path_of_link"
}

rm_old_link_file(){
    cmd="rm $f1_path_of_link"
}

do_linket_by_steps(){
    do_file_steps=("mv_target_file" "ln_new_target_file" "rm_old_link_file")

    for make_cmd_by_step in "${do_file_steps[@]}"
        do
        $make_cmd_by_step
        printf "$cmd \n"
            if ! $cmd
                then
                echo "NOTICE:: Operation failed."
                return 1
            fi
    done
}

rename_linkets(){
    length=${#arr_names_of_f2[@]}
    for (( i=0; $i<$length; i++ ))
        do
        echo $i
        
        f2_name=${arr_names_of_f2[$i]}
        expanded_f1_path="${arr_expanded_paths_of_f1[$i]}"    
        check_if_link_file_exist "$expanded_f1_path"
        if [ 0 -ne $? ]
            then
            continue
        fi

        make_paths_of_linket_in_oneloop
        do_linket_by_steps
    done

    #echo "arr_f1_paths_of_target:" ${arr_f1_paths_of_target[@]}
}

main(){
    check_dependency
    str_raw_paths_of_f1="$1"
    str_names_of_f2="$2"
    
    expand_path
    check_arg_nums "$@"
    rename_linkets
}

####################################
main "$@"
#!/bin/bash

# Test the usage of rename_linkets.bash
# Operation on some files would fail on purpose 
#     to show failure cases 

# use ./test_rename_linkets.bash -o 
#     to override the check on the test dir/files

echo_start(){
    echo "Start test $1 ============================="
}

init(){
    echo_start "$1"
    rm -r ./test9
    mkdir -p ./test9/dir1 ./test9/dir2
}

run_tests(){     
    if [[ -e ./test9 && "-o" != "$option" ]] 
        then
        echo "Dir test9 existed. Quit."
        exit 1 
    fi

    for test in "$@"
        do
        init "$test"
        $test
    done
}

test1(){
    touch ./test9/b.o
    touch ./test9/dir2/r.o
    
    ln -s ../dir2/t.o ./test9/dir1/t.o
    ln -s "../dir2/r.o" ./test9/dir1/r.o

    ./rename_linkets.bash "./test9/a.o test9/b.o ./test9/dir1/{t.o,r.o}" "a.n b.n t.n r.n"
}

test2(){
    if [ "-o" != "$option" ] 
        then
        for f in `echo "u.o u.n"`
            do
            echo "File $f existed. Quit."
            exit 1 
        done
    fi

    rm u.o u.n ./test9/u.o
    touch ./test9/u.o
    ln -s ./test9/u.o u.o 
    ./rename_linkets.bash u.o u.n
}

test3(){
    # test dangerous path 
    touch ./test9/dir2/dg.o
    ln -s ../dir2/dg.o ./test9/dir1/dg.o
    ./rename_linkets.bash ./test9/dir1/dg.o dg.n
}

option="$1"

#run_tests test1
#run_tests test2
#run_tests test3
run_tests test1 test2