4 ms·
Indeed, there are improvements that could be made. I took the easy way not only by using GNU extensions, but also by calling sed multiple times instead of just
by jolmg 5y ago
Indeed, there are improvements that could be made. I took the easy way not only by using GNU extensions, but also by calling sed multiple times instead of just once, and I even called awk at the end, though that was just because that bit didn't seem that significant. The spirit of the challenge wasn't exclusive to GNU, though. :)
I was even hoping there'd be comments that show a better solution to learn from them, GNU or not. I got an APL one at least, so that was cool.
- 1vuio0pswjnm7 5y agoHeres a solution that uses only sh and portable sed. No awk. No wc. This should work with GNU sed, BSD sed and Plan9 sed. #!/bin/sh col1(){ cal -y|sed -n "$1{s/\(.\{23\}\)\(.*\)/\1/p;}";} col2(){ cal -y|sed -n "$1{ s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/p;}";} col3(){ cal -y|sed -n "$1{ s/.\{46\}//p;}";} cal_y(){ for x in 3,10 11,18 19,26 27,34;do col1 "$x";col2 "$x";col3 "$x";done;} test $# -eq 4||exec echo usage: $0 7 jul 20 aug; x=$(echo $2 $4|sed -n '/[A-Z]/p'); if test "$x";then echo use lowercase;else cal_y|sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/ /Jan/,/Feb/s/$/ jan /; /Feb/,/Mar/s/$/ feb /; /Mar/,/Apr/s/$/ mar /; /Apr/,/May/s/$/ apr /; /May/,/Jun/s/$/ may /; /Jun/,/Jul/s/$/ jun /; /Jul/,/Aug/s/$/ jul /; /Aug/,/Sep/s/$/ aug /; /Sep/,/Oct/s/$/ sep /; /Oct/,/Nov/s/$/ oct /; /Nov/,/Dec/s/$/ nov /; /Dec/,$s/$/ dec /;/@/d' \ |sed -n "/ $1 .*$2/,/ $3 .*$4/{ / 1 /d;p;}"|sed -n '$=';fi
- jolmg 5y ago> Heres a solution that uses only sh and portable sed. No awk. No wc. No, it isn't! :) The challenge was specifically about joining the months of `cal -y` into a single column with sed. It's about the challenge of working with a grid of grids, with the restrictive nature of sed, having only one hold-space to hold 2 months separately. You totally missed doing that with your solution. Of course, it's loads easier to just skip the challenge altogether! Maybe the point of the sed challenge wasn't clear. It's about stretching the abilities of sed to its limits. Of being able to use all its features to its fullest. I think this is the first time that I had a real use for m~n, and I still haven't found a good use for `D` or `P`. EDIT: You're using `cal -y` now, but you've still gone outside the challenge space: > what kind of sed script I’d need in the middle of a `cal -y | ... | wc -l` pipeline to get the same answer Meaning the working space is just substituting the ellipses for `sed` commands. It's a single `cal -y` call, a single `wc -l` call, and no other shell code except pipes. I did an awk command in the last bit, but here is the sed equivalent: sed -E ' /[a-z]$/{h;s/.* //;x} /[a-z]$/!G /\b7\b.*July/,/20.*August/!d s/\n.*// ' Ideally, it was supposed to be just a single sed command, but I wanted to try out the org-mode blocks for showing the process in multiple stages and also use the m~n addresses, which it seemed like I wouldn't be able to if I did the single call.
- 1vuio0pswjnm7 5y agoThis solution uses temp files: 111, and 111-a through 111-l. 111 is the full calendar, the others are individual months. No dependence on any GNU sed features. Will work with GNU sed, BSD sed and Plan9 sed. Uses only sed commands and pipes, single cal -y, sed instead of wc. test $# -eq 4||exec echo usage: $0 7 jul 20 aug; x=$(echo $2 $4|sed -n '/[A-Z]/p'); if test "$x";then echo use lowercase;exit;fi cal -y \ |sed -n -e "w 111" \ -e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a }" -e "r 111"|sed -n \ -e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b }" -e "3,10r 111"|sed -n \ -e "3,10{s/.\{46\}//w 111-c }" -e "3,10r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d }" -e "11,18r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e }" -e "11,18r 111"|sed -n \ -e "11,18{s/.\{46\}//w 111-f }" -e "11,18r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g }" -e "19,26r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h }" -e "19,26r 111"|sed -n \ -e "19,26{s/.\{46\}//w 111-i }" -e "19,26r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j }" -e "27,34r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k }" -e "27,34r 111"|sed -n \ -e "27,34{s/.\{46\}//w 111-l }" -e "27,34r 111"|sed '2,$d' \ |sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \ -e "r 111-d" -e "r 111-e" -e "r 111-f" \ -e "r 111-g" -e "r 111-h" -e "r 111-i" \ -e "r 111-j" -e "r 111-k" -e "r 111-l" \ |sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/ /Jan/,/Feb/s/$/ jan /; /Feb/,/Mar/s/$/ feb /; /Mar/,/Apr/s/$/ mar /; /Apr/,/May/s/$/ apr /; /May/,/Jun/s/$/ may /; /Jun/,/Jul/s/$/ jun /; /Jul/,/Aug/s/$/ jul /; /Aug/,/Sep/s/$/ aug /; /Sep/,/Oct/s/$/ sep /; /Oct/,/Nov/s/$/ oct /; /Nov/,/Dec/s/$/ nov /; /Dec/,$s/$/ dec /;/@/d' \ |sed -n "/ $1 .*$2/,/ $3 .*$4/{ / 1 /d;p;}"|sed -n '$='; rm 111 111-[a-l]
- jolmg 5y agoThat's good! I didn't think to use the `r` and `w` commands. There's only the problem that because of `-n`, it doesn't output anything.
- 1vuio0pswjnm7 5y agoThis outputs the calendar dates to standard error. test $# -eq 4||exec echo usage: $0 7 jul 20 aug; x=$(echo $2 $4|sed -n '/[A-Z]/p'); if test "$x";then echo use lowercase;exit;fi cal -y \ |sed -n -e "w 111" \ -e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a }" -e "r 111"|sed -n \ -e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b }" -e "3,10r 111"|sed -n \ -e "3,10{s/.\{46\}//w 111-c }" -e "3,10r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d }" -e "11,18r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e }" -e "11,18r 111"|sed -n \ -e "11,18{s/.\{46\}//w 111-f }" -e "11,18r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g }" -e "19,26r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h }" -e "19,26r 111"|sed -n \ -e "19,26{s/.\{46\}//w 111-i }" -e "19,26r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j }" -e "27,34r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k }" -e "27,34r 111"|sed -n \ -e "27,34{s/.\{46\}//w 111-l }" -e "27,34r 111"|sed '2,$d' \ |sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \ -e "r 111-d" -e "r 111-e" -e "r 111-f" \ -e "r 111-g" -e "r 111-h" -e "r 111-i" \ -e "r 111-j" -e "r 111-k" -e "r 111-l" \ |sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/ /Jan/,/Feb/s/$/ jan /; /Feb/,/Mar/s/$/ feb /; /Mar/,/Apr/s/$/ mar /; /Apr/,/May/s/$/ apr /; /May/,/Jun/s/$/ may /; /Jun/,/Jul/s/$/ jun /; /Jul/,/Aug/s/$/ jul /; /Aug/,/Sep/s/$/ aug /; /Sep/,/Oct/s/$/ sep /; /Oct/,/Nov/s/$/ oct /; /Nov/,/Dec/s/$/ nov /; /Dec/,$s/$/ dec /;/@/d' \ |sed -n -e "/ $1 .*$2/,/ $3 .*$4/{ w /dev/stderr / 1 /d;p;}"|sed -n '$=' or to output the calendar dates to standard out, add another temp file, 111-z test $# -eq 4||exec echo usage: $0 7 jul 20 aug; x=$(echo $2 $4|sed -n '/[A-Z]/p'); if test "$x";then echo use lowercase;exit;fi cal -y \ |sed -n -e "w 111" \ -e "3,10{s/\(.\{23\}\)\(.*\)/\1/w 111-a }" -e "r 111"|sed -n \ -e "3,10{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-b }" -e "3,10r 111"|sed -n \ -e "3,10{s/.\{46\}//w 111-c }" -e "3,10r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.*\)/\1/w 111-d }" -e "11,18r 111"|sed -n \ -e "11,18{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-e }" -e "11,18r 111"|sed -n \ -e "11,18{s/.\{46\}//w 111-f }" -e "11,18r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.*\)/\1/w 111-g }" -e "19,26r 111"|sed -n \ -e "19,26{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-h }" -e "19,26r 111"|sed -n \ -e "19,26{s/.\{46\}//w 111-i }" -e "19,26r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.*\)/\1/w 111-j }" -e "27,34r 111"|sed -n \ -e "27,34{s/\(.\{23\}\)\(.\{20\}\)\(.*\)/\2/w 111-k }" -e "27,34r 111"|sed -n \ -e "27,34{s/.\{46\}//w 111-l }" -e "27,34r 111"|sed '2,$d' \ |sed -e "r 111-a" -e "r 111-b" -e "r 111-c" \ -e "r 111-d" -e "r 111-e" -e "r 111-f" \ -e "r 111-g" -e "r 111-h" -e "r 111-i" \ -e "r 111-j" -e "r 111-k" -e "r 111-l" \ |sed '/^Su/d;/^ *$/d;/^ *[A-S]/s/^/@/ /Jan/,/Feb/s/$/ jan /; /Feb/,/Mar/s/$/ feb /; /Mar/,/Apr/s/$/ mar /; /Apr/,/May/s/$/ apr /; /May/,/Jun/s/$/ may /; /Jun/,/Jul/s/$/ jun /; /Jul/,/Aug/s/$/ jul /; /Aug/,/Sep/s/$/ aug /; /Sep/,/Oct/s/$/ sep /; /Oct/,/Nov/s/$/ oct /; /Nov/,/Dec/s/$/ nov /; /Dec/,$s/$/ dec /;/@/d' \ |sed -n -e "/ $1 .*$2/,/ $3 .*$4/{ w 111-z / 1 /d;p;}"|sed -n -e '$=' -e "1r 111-z"
- saltcured 5y agoHere's the opposite of your approach, using only basic POSIX shell techniques... #!/bin/sh # trim left space triple ' '* lt() { s="$1"; while [ ! "${s## }" = "$s" ]; do s="${s## }"; done; printf "%s" "$s"; } # trim right space ' '* rt() { s="$1"; while [ ! "${s%% }" = "$s" ]; do s="${s%% }"; done; printf "%s" "$s"; } # count months for first/last special treatment n=1 # usage: reform pos line1 line2 ... line7 reform() { o=$1; m="$(printf "%s" $(expr substr "$2" $o 20))"; l1="$(expr substr "$3" $o 20)" [ $n -ne 1 ] && l1="$(lt "$l1")" # suffix of spliced week shift 3 printf "%s %s\n" "$l1" "$m" while [ "$#" -gt 0 ]; do l="$(expr substr "$1" $o 20 )"; lx="$(rt "$l")"; shift if [ -z "$lx" ]; then : elif [ "$l" = "$lx" -o $n -eq 12 ]; then printf "%s\n" "$lx" # whole week elif [ -n "$lx" ]; then printf "%s " "$lx" # prefix of spliced week fi done n=$(( $n + 1 )) } # buffer 8 lines into arg vector via recursion hack # then replace lines 3x to project out 3 months in this row buffer() { if [ "$#" -eq 7 ]; then reform 1 "$@"; reform 24 "$@"; reform 47 "$@" else IFS= read line || return 1 [ "$#" -eq 0 ] && read discard buffer "$@" "$line" fi } # strip preamble and process 4 rows of months process() { read discard; read discard; for i in 1 2 3 4; do buffer; done; } cal -y ${1:-2021} | process
- 1vuio0pswjnm7 5y agoexpr is not part of the shell, it is a separate program