1 ## compare_shells: dash bash mksh zsh
2 ## oils_failures_allowed: 4
3 ## oils_cpp_failures_allowed: 5
4 # case #24 with ulimit -f 1 is different under C++ for some reason - could be due to the python2
5 # intepreter and SIGXFSZ
6
7 #### exec builtin
8 exec echo hi
9 ## stdout: hi
10
11 #### exec builtin with redirects
12 exec 1>&2
13 echo 'to stderr'
14 ## stdout-json: ""
15 ## stderr: to stderr
16
17 #### exec builtin with here doc
18 # This has in a separate file because both code and data can be read from
19 # stdin.
20 $SH $REPO_ROOT/spec/bin/builtins-exec-here-doc-helper.sh
21 ## STDOUT:
22 x=one
23 y=two
24 DONE
25 ## END
26
27 #### exec builtin accepts --
28 exec -- echo hi
29 ## STDOUT:
30 hi
31 ## END
32 ## BUG dash status: 127
33 ## BUG dash stdout-json: ""
34
35 #### exec -- 2>&1
36 exec -- 3>&1
37 echo stdout 1>&3
38 ## STDOUT:
39 stdout
40 ## END
41 ## BUG dash status: 127
42 ## BUG dash stdout-json: ""
43 ## BUG mksh status: -11
44 ## BUG mksh stdout-json: ""
45
46 #### Exit out of function
47 f() { exit 3; }
48 f
49 exit 4
50 ## status: 3
51
52 #### Exit builtin with invalid arg
53 exit invalid
54 # Rationale: runtime errors are 1
55 ## status: 1
56 ## OK dash/bash status: 2
57 ## BUG zsh status: 0
58
59 #### Exit builtin with too many args
60 # This is a parse error in OSH.
61 exit 7 8 9
62 echo status=$?
63 ## status: 2
64 ## stdout-json: ""
65 ## BUG bash/zsh status: 0
66 ## BUG bash/zsh stdout: status=1
67 ## BUG dash status: 7
68 ## BUG dash stdout-json: ""
69 ## OK mksh status: 1
70 ## OK mksh stdout-json: ""
71
72 #### time with brace group argument
73
74 err=time-$(basename $SH).txt
75 {
76 time {
77 sleep 0.01
78 sleep 0.02
79 }
80 } 2> $err
81
82 grep --only-matching user $err
83 echo result=$?
84
85 # Regression: check fractional seconds
86 gawk '
87 BEGIN { ok = 0 }
88 match( $0, /\.([0-9]+)/, m) {
89 if (m[1] > 0) { # check fractional seconds
90 ok = 1
91 }
92 }
93 END { if (ok) { print "non-zero" } }
94 ' $err
95
96 ## status: 0
97 ## STDOUT:
98 user
99 result=0
100 non-zero
101 ## END
102
103 # time doesn't accept a block?
104 ## BUG zsh STDOUT:
105 result=1
106 ## END
107
108 # dash doesn't have time keyword
109 ## N-I dash status: 2
110 ## N-I dash stdout-json: ""
111
112
113 #### get umask
114 umask | grep '[0-9]\+' # check for digits
115 ## status: 0
116
117 #### set umask in octal
118 rm -f $TMP/umask-one $TMP/umask-two
119 umask 0002
120 echo one > $TMP/umask-one
121 umask 0022
122 echo two > $TMP/umask-two
123 stat -c '%a' $TMP/umask-one $TMP/umask-two
124 ## status: 0
125 ## STDOUT:
126 664
127 644
128 ## END
129 ## stderr-json: ""
130
131 #### set umask symbolically
132 umask 0002 # begin in a known state for the test
133 rm -f $TMP/umask-one $TMP/umask-two
134 # open()s 'umask-one' with mask 0666, then subtracts 0002 -> 0664
135 echo one > $TMP/umask-one
136 umask g-w,o-w
137 echo two > $TMP/umask-two
138 stat -c '%a' $TMP/umask-one $TMP/umask-two
139 ## status: 0
140 ## STDOUT:
141 664
142 644
143 ## END
144 ## stderr-json: ""
145
146 #### umask -p
147 umask -p | grep 'umask [0-9][0-9][0-9][0-9]'
148 ## status: 0
149 ## N-I dash/mksh/zsh status: 1
150
151 #### umask -S
152 # current mask as symbolic
153 umask -S | grep 'u=[rwx]*,g=[rwx]*,o=[rwx]*'
154 ## status: 0
155
156 #### umask symbolic parsing
157
158 umask 0000
159 umask u-rw
160 echo status0=$?
161 umask | tail -c 4
162
163 umask 0700
164 umask u=r
165 echo status1=$?
166 umask | tail -c 4
167
168 umask 0000
169 umask u=r,g=w,o=x
170 echo status2=$?
171 umask | tail -c 4
172
173 umask 0777
174 umask u+r,g+w,o+x
175 echo status3=$?
176 umask | tail -c 4
177
178 umask 0000
179 umask u-r,g-w,o-x
180 echo status4=$?
181 umask | tail -c 4
182
183 umask 0137
184 umask u=,g+,o-
185 echo status5=$?
186 umask | tail -c 4
187
188 ## status: 0
189 ## STDOUT:
190 status0=0
191 600
192 status1=0
193 300
194 status2=0
195 356
196 status3=0
197 356
198 status4=0
199 421
200 status5=0
201 737
202 ## END
203
204 #### umask symbolic parsing with spaces
205 umask 0111
206 # spaces are an error in bash
207 # dash & mksh only interpret the first one
208 umask u=, g+, o-
209 echo status=$?
210 umask | tail -c 4
211 ## status: 0
212 ## STDOUT:
213 status=2
214 111
215 ## END
216 ## OK bash/zsh STDOUT:
217 status=1
218 111
219 ## END
220 ## BUG dash/mksh STDOUT:
221 status=0
222 711
223 ## END
224
225 #### umask bad symbolic input
226 umask b=rwx
227 ## status: 1
228 ## OK dash status: 2
229
230 #### umask octal number out of range
231 umask 0022
232 umask 1234567
233 # osh currently treats 0o1234567 as 0o0567
234 echo status=$?
235 umask | tail -c 4
236 ## status: 0
237 ## STDOUT:
238 status=1
239 022
240 ## END
241 ## BUG mksh/zsh/dash STDOUT:
242 status=0
243 567
244 ## END
245
246 #### umask allow overwriting and duplicates
247 umask 0111
248 umask u=rwx,u=rw,u=r,u=,g=rwx
249 umask | tail -c 4
250 ## status: 0
251 ## STDOUT:
252 701
253 ## END
254
255 #### umask a is valid who
256 umask 0732
257 umask a=rwx
258 umask | tail -c 4
259
260 umask 0124
261 umask a+r
262 umask | tail -c 4
263
264 umask 0124
265 umask a-r
266 umask | tail -c 4
267 ## status: 0
268 ## STDOUT:
269 000
270 120
271 564
272 ## END
273
274 #### umask X perm
275 umask 0124
276 umask a=X
277 echo ret0 = $?
278 umask | tail -c 4
279
280 umask 0246
281 umask a=X
282 echo ret1 = $?
283 umask | tail -c 4
284
285 umask 0246
286 umask a-X
287 echo ret2 = $?
288 umask | tail -c 4
289 ## status: 0
290 ## STDOUT:
291 ret0 = 0
292 666
293 ret1 = 0
294 777
295 ret2 = 0
296 246
297 ## END
298 ## BUG dash/mksh STDOUT:
299 ret0 = 0
300 666
301 ret1 = 0
302 666
303 ret2 = 0
304 357
305 ## END
306 ## N-I bash/zsh STDOUT:
307 ret0 = 1
308 124
309 ret1 = 1
310 246
311 ret2 = 1
312 246
313 ## END
314
315 #### umask s perm
316 umask 0124
317 umask a-s
318 echo ret0 = $?
319 umask | tail -c 4
320
321 umask 0124
322 umask a+s
323 echo ret1 = $?
324 umask | tail -c 4
325
326 umask 0124
327 umask a=s
328 echo ret2 = $?
329 umask | tail -c 4
330 ## status: 0
331 ## STDOUT:
332 ret0 = 0
333 124
334 ret1 = 0
335 124
336 ret2 = 0
337 777
338 ## END
339 ## N-I bash/zsh STDOUT:
340 ret0 = 1
341 124
342 ret1 = 1
343 124
344 ret2 = 1
345 124
346 ## END
347
348 #### umask t perm
349 umask 0124
350 umask a-t
351 echo ret0 = $?
352 umask | tail -c 4
353
354 umask 0124
355 umask a+t
356 echo ret1 = $?
357 umask | tail -c 4
358
359 umask 0124
360 umask a=t
361 echo ret2 = $?
362 umask | tail -c 4
363 ## status: 0
364 ## STDOUT:
365 ret0 = 0
366 124
367 ret1 = 0
368 124
369 ret2 = 0
370 777
371 ## END
372 ## N-I bash/zsh/mksh STDOUT:
373 ret0 = 1
374 124
375 ret1 = 1
376 124
377 ret2 = 1
378 124
379 ## END
380 ## N-I dash STDOUT:
381 ret0 = 2
382 124
383 ret1 = 2
384 124
385 ret2 = 2
386 124
387 ## END
388
389 #### umask default who
390 umask 0124
391 umask =
392 umask | tail -c 4
393
394 umask 0124
395 umask =rx
396 echo ret = $?
397 umask | tail -c 4
398
399 umask 0124
400 umask +
401 umask | tail -c 4
402
403 umask 0124
404 # zsh ALSO treats this as just `umask`
405 umask - >/dev/null
406 umask | tail -c 4
407 ## status: 0
408 ## BUG zsh status: 1
409 ## STDOUT:
410 777
411 ret = 0
412 222
413 124
414 124
415 ## END
416 ## BUG zsh STDOUT:
417 777
418 ## END
419
420 #### umask bare op
421 umask 0124
422 umask =+=
423 umask | tail -c 4
424
425 umask 0124
426 umask +=
427 umask | tail -c 4
428
429 umask 0124
430 umask =+rwx+rx
431 umask | tail -c 4
432 ## status: 0
433 ## BUG zsh status: 1
434 ## STDOUT:
435 777
436 777
437 000
438 ## END
439 ## N-I bash STDOUT:
440 124
441 124
442 124
443 ## END
444 ## BUG zsh STDOUT:
445 ## END
446
447 #### umask bare op -
448 umask 0124
449 umask -rwx
450 umask | tail -c 4
451
452 umask 0124
453 umask -wx
454 umask | tail -c 4
455
456 umask 0124
457 umask -=+
458 umask | tail -c 4
459 ## status: 0
460 ## STDOUT:
461 777
462 337
463 777
464 ## END
465 ## N-I dash/bash/mksh/zsh STDOUT:
466 124
467 124
468 124
469 ## END
470
471 #### umask permcopy
472 umask 0124
473 umask a=u
474 umask | tail -c 4
475
476 umask 0365
477 umask a=g
478 umask | tail -c 4
479
480 umask 0124
481 umask a=o
482 umask | tail -c 4
483 ## status: 0
484 ## STDOUT:
485 111
486 666
487 444
488 ## END
489 ## N-I bash/zsh STDOUT:
490 124
491 365
492 124
493 ## END
494
495 #### umask permcopy running value
496 umask 0124
497 umask a=,a=u
498 umask | tail -c 4
499
500 umask 0124
501 umask a=
502 umask a=u
503 umask | tail -c 4
504 ## status: 0
505 ## STDOUT:
506 111
507 777
508 ## END
509 ## N-I bash/zsh STDOUT:
510 124
511 777
512 ## END
513
514 #### umask sequential actions
515 umask 0124
516 umask u+r+w+x
517 umask | tail -c 4
518
519 umask 0124
520 umask a+r+w+x,o-w
521 umask | tail -c 4
522
523 umask 0124
524 umask a+x+wr-r
525 umask | tail -c 4
526 ## status: 0
527 ## STDOUT:
528 024
529 002
530 444
531 ## END
532 ## N-I bash/zsh STDOUT:
533 124
534 124
535 124
536 ## END
537
538
539 #### ulimit with no flags is like -f
540
541 ulimit > no-flags.txt
542 echo status=$?
543
544 ulimit -f > f.txt
545 echo status=$?
546
547 diff -u no-flags.txt f.txt
548 echo diff=$?
549
550 # Print everything
551 # ulimit -a
552
553 ## STDOUT:
554 status=0
555 status=0
556 diff=0
557 ## END
558
559
560 #### ulimit too many args
561
562 ulimit 1 2
563 if test $? -ne 0; then
564 echo pass
565 else
566 echo fail
567 fi
568
569 #ulimit -f
570
571 ## STDOUT:
572 pass
573 ## END
574
575 ## BUG bash/zsh STDOUT:
576 fail
577 ## END
578
579
580 #### ulimit negative flag
581
582 ulimit -f
583
584 # interpreted as a flag
585 ulimit -f -42
586 if test $? -ne 0; then
587 echo pass
588 else
589 echo fail
590 fi
591
592 ## STDOUT:
593 unlimited
594 pass
595 ## END
596
597 #### ulimit negative arg
598
599 ulimit -f
600
601 # an arg
602 ulimit -f -- -42
603 if test $? -ne 0; then
604 echo pass
605 else
606 echo fail
607 fi
608
609 ## STDOUT:
610 unlimited
611 pass
612 ## END
613
614 ## BUG mksh STDOUT:
615 unlimited
616 fail
617 ## END
618
619
620 #### ulimit -a doesn't take arg
621 case $SH in bash) exit ;; esac
622
623 ulimit -a 42
624 if test $? -ne 0; then
625 echo 'failure that was expected'
626 fi
627
628 ## STDOUT:
629 failure that was expected
630 ## END
631 ## BUG bash STDOUT:
632 ## END
633
634
635 #### ulimit doesn't accept multiple flags - reduce confusion between shells
636
637 # - bash, zsh, busybox ash accept multiple "commands", which requires custom
638 # flag parsing, like
639
640 # ulimit -f 999 -n
641 # ulimit -f 999 -n 888
642 #
643 # - dash and mksh accept a single ARG
644 #
645 # we want to make it clear we're like the latter
646
647 # can't print all and -f
648 ulimit -f -a >/dev/null
649 echo status=$?
650
651 ulimit -f -n >/dev/null
652 echo status=$?
653
654 ulimit -f -n 999 >/dev/null
655 echo status=$?
656
657 ## STDOUT:
658 status=2
659 status=2
660 status=2
661 ## END
662
663 ## BUG dash/bash/mksh STDOUT:
664 status=0
665 status=0
666 status=0
667 ## END
668
669 # zsh is better - it checks that -a and -f are exclusive
670
671 ## BUG zsh STDOUT:
672 status=1
673 status=0
674 status=0
675 ## END
676
677
678 #### YSH readability: ulimit --all the same as ulimit -a
679
680 case $SH in bash|dash|mksh|zsh) exit ;; esac
681
682 ulimit -a > short.txt
683 ulimit --all > long.txt
684
685 wc -l short.txt long.txt
686
687 diff -u short.txt long.txt
688 echo status=$?
689
690 ## STDOUT:
691 8 short.txt
692 8 long.txt
693 16 total
694 status=0
695 ## END
696
697 ## N-I bash/dash/mksh/zsh STDOUT:
698 ## END
699
700 #### ulimit accepts 'unlimited'
701
702 for arg in zz unlimited; do
703 echo " arg $arg"
704 ulimit -f
705 echo status=$?
706 ulimit -f $arg
707 if test $? -ne 0; then
708 echo 'FAILED'
709 fi
710 echo
711 done
712 ## STDOUT:
713 arg zz
714 unlimited
715 status=0
716 FAILED
717
718 arg unlimited
719 unlimited
720 status=0
721
722 ## END
723
724
725 #### ulimit of 2**32, 2**31 (int overflow)
726
727 echo -n 'one '; ulimit -f
728
729
730 ulimit -f $(( 1 << 32 ))
731
732 echo -n 'two '; ulimit -f
733
734
735 # mksh fails because it overflows signed int, turning into negative number
736 ulimit -f $(( 1 << 31 ))
737
738 echo -n 'three '; ulimit -f
739
740 ## STDOUT:
741 one unlimited
742 two 4294967296
743 three 2147483648
744 ## END
745 ## BUG mksh STDOUT:
746 one unlimited
747 two 1
748 three 1
749 ## END
750
751
752 #### ulimit that is 64 bits
753
754 # no 64-bit integers
755 case $SH in mksh) exit ;; esac
756
757 echo -n 'before '; ulimit -f
758
759 # 1 << 63 overflows signed int
760
761 # 512 is 1 << 9, so make it 62-9 = 53 bits
762
763 lim=$(( 1 << 53 ))
764 #echo $lim
765
766 # bash says this is out of range
767 ulimit -f $lim
768
769 echo -n 'after '; ulimit -f
770
771 ## STDOUT:
772 before unlimited
773 after 9007199254740992
774 ## END
775
776 ## BUG mksh STDOUT:
777 ## END
778
779
780 #### arg that would overflow 64 bits is detected
781
782 # no 64-bit integers
783 case $SH in mksh) exit ;; esac
784
785 echo -n 'before '; ulimit -f
786
787 # 1 << 63 overflows signed int
788
789 lim=$(( (1 << 62) + 1 ))
790 #echo lim=$lim
791
792 # bash detects that this is out of range
793 # so does osh-cpp, but not osh-cpython
794
795 ulimit -f $lim
796 echo -n 'after '; ulimit -f
797
798 ## STDOUT:
799 before unlimited
800 after unlimited
801 ## END
802
803 ## BUG dash/zsh STDOUT:
804 before unlimited
805 after 1
806 ## END
807
808 ## BUG mksh STDOUT:
809 ## END
810
811
812 #### ulimit -f 1 prevents files larger 512 bytes
813 trap - XFSZ # don't handle this
814
815 rm -f err.txt
816 touch err.txt
817
818 bytes() {
819 local n=$1
820 local st=0
821 for i in $(seq $n); do
822 echo -n x
823 st=$?
824 if test $st -ne 0; then
825 echo "ERROR: echo failed with status $st" >> err.txt
826 fi
827 done
828 }
829
830 ulimit -f 1
831
832 bytes 512 > ok.txt
833 echo 512 status=$?
834
835 bytes 513 > too-big.txt
836 echo 513 status=$?
837 echo
838
839 wc --bytes ok.txt too-big.txt
840 echo
841
842 cat err.txt
843
844 ## status: -25
845 ## STDOUT:
846 512 status=0
847 ## END
848
849 ## OK disabledosh status: 0
850 ## OK disabledosh STDOUT:
851 512 status=0
852 513 status=0
853
854 512 ok.txt
855 512 too-big.txt
856 1024 total
857
858 ERROR: echo failed with status 1
859 ## END
860
861 ## BUG bash status: 0
862 ## BUG bash STDOUT:
863 512 status=0
864 513 status=0
865
866 512 ok.txt
867 513 too-big.txt
868 1025 total
869
870 ## END
871
872 #### write big file with ulimit
873
874 # I think this will test write() errors, rather than the final flush() error
875 # (which is currently skipped by C++
876
877 { echo 'ulimit -f 1'
878 # More than 8 KiB may cause a flush()
879 python2 -c 'print("echo " + "X"*9000 + " >out.txt")'
880 echo 'echo inner=$?'
881 } > big.sh
882
883 $SH big.sh
884 echo outer=$?
885
886 ## STDOUT:
887 outer=153
888 ## END
889
890 # not sure why this is different
891 ## OK osh STDOUT:
892 inner=1
893 outer=0
894 ## END
895
896
897 #### ulimit -S for soft limit (default), -H for hard limit
898 case $SH in dash|zsh) exit ;; esac
899
900 # Note: ulimit -n -S 1111 is OK in osh/dash/mksh, but not bash/zsh
901 # Mus be ulimit -S -n 1111
902
903 show_state() {
904 local msg=$1
905 echo "$msg"
906 echo -n ' '; ulimit -S -t
907 echo -n ' '; ulimit -H -t
908 echo
909 }
910
911 show_state 'init'
912
913 ulimit -S -t 123456
914 show_state '-S'
915
916 ulimit -H -t 123457
917 show_state '-H'
918
919 ulimit -t 123455
920 show_state 'no flag'
921
922 echo 'GET'
923
924 ulimit -S -t 123454
925 echo -n ' '; ulimit -t
926 echo -n ' '; ulimit -S -t
927 echo -n ' '; ulimit -H -t
928
929 ## STDOUT:
930 init
931 unlimited
932 unlimited
933
934 -S
935 123456
936 unlimited
937
938 -H
939 123456
940 123457
941
942 no flag
943 123455
944 123455
945
946 GET
947 123454
948 123454
949 123455
950 ## END
951
952 ## BUG dash/zsh STDOUT:
953 ## END
954
955 #### Changing resource limit is denied
956
957 # Not sure why these don't work
958 case $SH in dash|mksh) exit ;; esac
959
960
961 flag=-t
962
963 ulimit -S -H $flag 100
964 echo both=$?
965
966 ulimit -S $flag 90
967 echo soft=$?
968
969 ulimit -S $flag 95
970 echo soft=$?
971
972 ulimit -S $flag 105
973 if test $? -ne 0; then
974 echo soft OK
975 else
976 echo soft fail
977 fi
978
979 ulimit -H $flag 200
980 if test $? -ne 0; then
981 echo hard OK
982 else
983 echo hard fail
984 fi
985
986 ## STDOUT:
987 both=0
988 soft=0
989 soft=0
990 soft OK
991 hard OK
992 ## END
993
994 ## BUG dash/mksh STDOUT:
995 ## END
996
997 #### ulimit -n limits file descriptors
998
999 # OSH bug
1000 # https://oilshell.zulipchat.com/#narrow/channel/502349-osh/topic/alpine.20build.20failures.20-.20make.20-.20ulimit.20-n.2064/with/519691301
1001
1002 $SH -c 'ulimit -n 64; echo hi >out'
1003 echo status=$?
1004
1005 $SH -c 'ulimit -n 0; echo hi >out'
1006 echo status=$?
1007
1008 ## STDOUT:
1009 status=0
1010 status=1
1011 ## END
1012
1013 ## OK dash STDOUT:
1014 status=0
1015 status=2
1016 ## END