How to avoid magic-numbers in shell?

时间:2017-08-05 11:36:04

标签: linux bash shell unix magic-numbers

I always write some magic numbers in my interactive shells and shell scripts.

For instance, If I want to list my users's names and shells, I'll write

cut --delimiter=: --fields=1,7 /etc/passwd

There exist two magic-numbers 1,7. And there are more and more magic-numbers in other circumstances.

Question

How to avoid magic-numbers in interactive shells and shell scripts?

Supplementary background

Our teacher told us using cut -d: -f1,7 /etc/passwd. But for new linux-users, they don't konw what's meaning of d,f,1,7.(not just for new linux-users,the whole system has so many configuration files that it is not easy for a person to remember every magic-numbers)

So, in interactive shells, we can use --delimiter, --fields,and the bash repl(or zsh,fish) has good tab completion to it.

How about the 1 and 7? In shell scripts, It's a good method to declare some const variables like LoginField=1 and ShellField=7 after reading the man 5 passwd. But when some one is writing in the interactive shells, it's not a good idea to open a new window and search the constants of LoginField=1,ShellField=7 and define it. how to using some thing like tab completion to simplify operations?

6 个答案:

答案 0 :(得分:1)

Use variables:

LoginField=1 ShellField=7
cut --delimiter=: --fields="$LoginField,$ShellField" /etc/passwd

答案 1 :(得分:1)

Just like in other languages - by using variables. Example:

$ username_column=1
$ shell_column=7 
$ cut --delimiter=: --fields="$username_column","$shell_column" /etc/passwd

The variables may be defined at the top of the script so that can be easily modified or they can be set in an external config-like file shared by multiple scripts.

答案 2 :(得分:1)

解析/ etc / passwd的经典方法是将每列读入一个适当命名的变量:

while IFS=: read name passwd uid gid gecos home shell _; do 
   ...
done < /etc/passwd

答案 3 :(得分:0)

Use export: export field_param="1,7" (you can put it .bashrc file to have configured each time shell session is started). This export can be part of .sh script. It's a good practice to put them in the head/top of the file. Then: cut --delimiter=: --fields=$field_param /etc/passwd This way you will need to edit the magic number in the only location.

答案 4 :(得分:0)

Continuing from my comment, it's hard to tell exactly what you are asking. If you just want to give meaningful variable names, then do as shown in the other answers.

If however you want to be able to specify which fields are passed to cut from the command line, then you can use the positional parameters $1 and $2 to pass those values into your script.

You need to validate that two inputs are given and that both are integers. You can do that with a few simple tests, e.g.

#!/bin/bash

[ -n "$1" ] && [ -n "$2" ] || { ## validate 2 parameters given
    printf "error: insufficient input\nusage: %s field1 field2\n" "${0##*/}"
    exit 1
}

## validate both inputs are integer values
[ "$1" -eq "$1" >/dev/null 2>&1 ] || {
    printf "error: field1 not integer value '%s'.\n" "$1"
    exit 1
}

[ "$2" -eq "$2" >/dev/null 2>&1 ] || {
    printf "error: field2 not integer value '%s'.\n" "$2"
    exit 1
}

cut --delimiter=: --fields=$1,$2 /etc/passwd

Example Use/Output

$ bash fields.sh
error: insufficient input
usage: fields.sh field1 field2

$  bash fields.sh 1 d
error: field2 not integer value 'd'.

$  bash fields.sh 1 7
root:/bin/bash
bin:/usr/bin/nologin
daemon:/usr/bin/nologin
mail:/usr/bin/nologin
ftp:/usr/bin/nologin
http:/usr/bin/nologin
uuidd:/usr/bin/nologin
dbus:/usr/bin/nologin
nobody:/usr/bin/nologin
systemd-journal-gateway:/usr/bin/nologin
systemd-timesync:/usr/bin/nologin
systemd-network:/usr/bin/nologin
systemd-bus-proxy:/usr/bin/nologin
<snip>

Or if you choose to look at fields 1 and 3, then all you need do is pass those as the parameters, e.g.

$  bash fields.sh 1 3
root:0
bin:1
daemon:2
mail:8
ftp:14
http:33
uuidd:68
dbus:81
nobody:99
systemd-journal-gateway:191
systemd-timesync:192
systemd-network:193
systemd-bus-proxy:194
<snip>

Look things over and let me know if you have further questions.

答案 5 :(得分:0)

为人类可读的标题名称刮取man 5 passwd的输出:

declare $(man 5 passwd | 
          sed -n '/^\s*·\s*/{s/^\s*·\s*//;y/ /_/;p}' | 
          sed -n 'p;=' | paste -d= - - )

参见&#34; 工作原理&#34;在下面做什么,然后运行:

cut --delimiter=: \
    --fields=${login_name},${optional_user_command_interpreter} /etc/passwd

输出指定的/etc/passwd字段。

工作原理。

描述man的{​​{1}}页面包含标题名称的项目符号列表。使用 GNU /etc/passwd查找项目符号(sed)和前导空格,然后删除项目符号和空格,用下划线替换剩余的空格; ·第二个实例提供了新的行号,然后sed标题名称为行号,其中paste位于:

=

输出:

man 5 passwd | 
sed -n '/^\s*·\s*/{s/^\s*·\s*//;y/ /_/;p}' | 
sed -n 'p;=' | paste -d= - -

login_name=1 optional_encrypted_password=2 numerical_user_ID=3 numerical_group_ID=4 user_name_or_comment_field=5 user_home_directory=6 optional_user_command_interpreter=7 会使当前shell中的那些活动。