We had to give the names of 15 commands matching `\w+` , 4 of them from `php, python3, ruby, bash, node`, and a file `food` such that the outputs of `cat food` and `<command> food` match, checked with `timeout 5 diff -Z <(cat food) <(eachCatNameYouProvided food)`, executed in a docker box:
```FROM ubuntu:latestENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezoneRUN apt-get update && apt-get install -y php python3 nodejs ruby && mkdir /appWORKDIR /app
# build: docker build -t rctf_cats .# judge: docker run -it --rm --network none -v /tmp/yourCatFood:/app/food:ro rctf_cats bash -c "timeout 5 diff -Z <(cat food) <(eachCatNameYouProvided food)"```
So we need to write a fake quine that works in `php, python3, ruby, bash, node`.
```"true$(cat food > /proc/$$/fd/1)"exit=1exita = ["require('fs').readFile('food', 'utf8', (err, data) => console.log(data))", "print (0 and File.read('food') or open('food').read())"]eval(a[10000000000000001 - 10000000000000000])```
PHP is easy. As long as we don't have a php tag, it just prints out the file.Let us go over the lines:
1. In bash `$(cat food > /proc/$$/fd/1)` writes the contents of `food` into the standard output of bash: `$$` expands to the process id of bash and file descriptor 1 is the standard output. Then `$(cat food > /proc/$$/fd/1)` expands to the empty string and `true` is called which does nothing. In python, ruby and node this line is just treated as a string2. Define (for node) or overwrite (for python and ruby) `exit` such that line 3 neither fails nor exits.3. Exit in bash. Just an expression in the other languages.4. Define two strings to be evaluated. The first one is for node and just prints the file `food`. The second is a polyglot for python and ruby. It takes advantage of the short circuit operators `and` and `or` together with the fact that a 0 is truthy in ruby and falsy in python. Hence, in ruby `File.read('food')` is evaluated and in python `open('food').read()`.5. Evaluate `a[0]` in node in `a[1]` in python and ruby. `10000000000000001 - 10000000000000000` is 0 in node due the lack of precision in large floating point numbers. Python and ruby support big integers natively.
Together with aliases, we now have "cats"```node, js, nodejsphppython3, python3msh, dash, bashruby```For the remaining 5 we chose `paste, more, tail, head, uniq`; they just print the file contents, in this case.
## Improved version
After the contest we came up with:
```cat=1;food=1;cat < food;exit=1;exit;a=["require('fs').readFile('food', 'utf8', (err, data) => console.log(data))", "print (0 and File.read('food') or open('food').read())"]eval(a[10000000000000001 - 10000000000000000])```
This allows us to add `rbash` (`$()` is not allowed in restricted mode), `sort` and `shuf` (with 50% chance of working; the linebreak appears to be necessary as `a[...]` is invalid bash syntax).