-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathrcut
executable file
·156 lines (133 loc) · 3.76 KB
/
rcut
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/usr/bin/env bash
# use mawk by default
# will be changed to gawk if -g option is used
cmd='mawk'
# similar to grep's -F option
# treats the input field separator(IFS) literally
F=''
# initialize input and output field separators as single space
# which is same default as awk
d=' '
o=' '
# - means print all fields
# this default helps to format input with value of output field separator(OFS)
f='-'
# allow negative indexing if set
n=''
# complement fields if set
c=''
# suppress lines not matching IFS
s=''
# flag variable to check if output field separator was set by the user or not
ofs_flag='false'
# first : makes it silent error reporting mode
# d f o options require arguments if used
# n c g s F options do not accept arguments
while getopts ":d:f:o:ncgsF" opt; do
case $opt in
d)
d="$OPTARG"
;;
f)
f="$OPTARG"
;;
o)
o="$OPTARG"
ofs_flag='true'
;;
n)
n=':'
;;
c)
c='1'
;;
g)
cmd='gawk'
;;
s)
s='1'
;;
F)
F='1'
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done
# Discard the options and sentinel --
shift "$((OPTIND-1))"
# argument to -f option cannot be empty
# it can have only digits comma and hyphen characters
# if -n is active, colon is also allowed
if [[ ! "$f" =~ ^[${n}0-9,-]+$ ]]; then
echo "Field number can only use integer values" >&2
exit 1
fi
# if -o option isn't used, two cases where OFS will set to same value as IFS
# 1) if -F option is used
# 2) if IFS is a single character (partially mimics cut command behavior)
# \ character needs to be doubled since it is a metacharacter in awk strings
if [[ "$ofs_flag" == 'false' && ( ${#d} == 1 || "$F" == '1' ) ]] ; then
o="${d//\\/\\\\}"
fi
# if -F option is used, change IFS such that it matches literally
if [[ "$F" == '1' ]] ; then
d="$(echo "$d" | "$cmd" '{gsub(/[^^\\]/, "[&]");
gsub(/\\/, "\\x5c\\x5c");
gsub(/\^/, "\\x5c\\x5e")} 1')"
fi
# corner case: when -o is \ character (needed for gawk)
# since -o is used, this isn't covered by earlier \ doubling
if [[ "$o" == '\' ]] ; then
o='\\'
fi
"$cmd" -F "$d" -v fields="$f" -v OFS="$o" -v neg="$n" -v complement="$c" -v suppress="$s" '
BEGIN{
fn = split(fields, fields_arr, /,/)
}
{
if(suppress && NF<=1)
next
sep = ""
for(i=1; i<=fn; i++){
if(neg){
rn = split(fields_arr[i], range, /:/)
if(range[1] ~ /^-/) range[1] += NF + 1
if(range[2] ~ /^-/) range[2] += NF + 1
}
else
rn = split(fields_arr[i], range, /-/)
start = range[1] <= 0 ? 1: range[1]
if(start > NF) start = NF
if(rn == 1)
end = start
else if(range[2] == "" || range[2] > NF)
end = NF
else if(range[2] <= 0)
end = 1
else
end = range[2]
for(j=start; j<=end; j++)
if(complement)
ignore_fields[j]
else {
printf "%s%s", sep, $j
sep = OFS
}
}
if(complement){
for(k=1; k<=NF; k++)
if(!(k in ignore_fields)){
printf "%s%s", sep, $k
sep = OFS
}
delete ignore_fields
}
print ""
}' "$@"