18
\$\begingroup\$

Objective

You are to write a program that receives an integer n as input (from the command line), and embeds itself (the program) n directories down the directory tree. Example with n=5:

Example with n = 5

The folder names may be whatever you wish. The only requirements are that the depth is correct, and that the program can then be ran again from its new spot in the directory tree, and that the new source file retains the same filename.

Bonuses:

  • Score * 0.9 If the directories all have a different name (must be true at least to depth 1 000 000)
  • Score * 0.5 If you do not directly or indirectly read or move the source file, or access the source code of the program
\$\endgroup\$
16
  • 1
    \$\begingroup\$ What counts as "reading the source"? You mean the file? Or the real source code? \$\endgroup\$
    – GiantTree
    Jan 5, 2015 at 0:04
  • 2
    \$\begingroup\$ @unclemeat It does, because to move the file you (or the system) has to access the data in the file. \$\endgroup\$
    – globby
    Jan 5, 2015 at 2:05
  • 25
    \$\begingroup\$ Seems like you are just trying to hide your porn stash. \$\endgroup\$
    – Matt
    Jan 5, 2015 at 3:57
  • 3
    \$\begingroup\$ @globby how about the ln command in *nix? If I'm not mistaken, it's just creating another entry to the file inode, and no content is read at all. \$\endgroup\$
    – h.j.k.
    Jan 5, 2015 at 8:18
  • 7
    \$\begingroup\$ @globby As far as I'm aware, moving a file does not read the contents, unless you're moving between hard drives or partitions. It's basically just changing some pointers in the file system. \$\endgroup\$ Jan 5, 2015 at 9:10

10 Answers 10

36
\$\begingroup\$

Bash, 30*0.9*0.5 = 13.5

mkdir -p `seq -s/ $1`;ln $0 $_

Takes depth as the first argument. Creates a hard link to itself into the following directory structure:

1/2/3/4/5/.../n

The script may then be run from the new location, even if rm is run on the old script.

Explanation:

seq -s/ $1 outputs the numbers from 1 to $1 (the first argument), separated by a forward slash.

mkdir -p `seq -s` $1 creates the directory specified by seq, with -p creating all intermediate directories.

ln $0 $_ create a hard link to the current running script in the newly created directory.

Old (30 * 0.9 = 27):

mkdir -p `seq -s/ $1`;cp $0 $_

Example run (with ln):

$ ls -lGR
.:
total 1
-rwx------+ 1 ducks 41 Jan  5 15:00 test.sh

$ ./test.sh 4

$ ls -lgR
.:
total 1
drwxr-xr-x+ 1 ducks  0 Jan  5 15:01 1
-rwx------+ 2 ducks 41 Jan  5 15:00 test.sh

./1:
total 0
drwxr-xr-x+ 1 ducks 0 Jan  5 15:01 2

./1/2:
total 0
drwxr-xr-x+ 1 ducks 0 Jan  5 15:01 3

./1/2/3:
total 0
drwxr-xr-x+ 1 ducks 0 Jan  5 15:01 4

./1/2/3/4:
total 1
-rwx------+ 2 ducks 41 Jan  5 15:00 test.sh

$ rm ./test.sh

$ ls -lg
total 0
drwxr-xr-x+ 1 ducks 0 Jan  5 15:01 1

$ ls -lg 1/2/3/4
total 1
-rwx------+ 1 ducks 41 Jan  5 15:00 test.sh

Thanks to @DigitalTrauma for suggestion to replace $(..) with `..`

Thanks to @h.j.k. for suggestion to use ln.

\$\endgroup\$
5
  • 6
    \$\begingroup\$ Brilliant use of $_! \$\endgroup\$
    – wchargin
    Jan 5, 2015 at 3:04
  • 2
    \$\begingroup\$ Save a char - use backticks instead of $( ): codegolf.stackexchange.com/a/25572/11259 \$\endgroup\$ Jan 5, 2015 at 5:32
  • 2
    \$\begingroup\$ Depending on the answer to my comment on the question, maybe replace cp with ln to get the 0.5 bonus too... \$\endgroup\$
    – h.j.k.
    Jan 5, 2015 at 8:20
  • 2
    \$\begingroup\$ Wells, there's the update from the OP now, and mv is also restricted from that 0.5 bonus. That still leaves ln in the clear, yes? \$\endgroup\$
    – h.j.k.
    Jan 5, 2015 at 17:38
  • 2
    \$\begingroup\$ Yes, ln (you don't need -s) would actually place the program in the new directory, so that it can be executed from there, without ever reading, moving, or accessing its original source. I say go for the FALCON PUNCH score of 13.5! \$\endgroup\$
    – Tobia
    Jan 5, 2015 at 22:45
12
\$\begingroup\$

C, 225 * 0.9 * 0.5 = 101.25

My solution in C:

$ cat d.c
#define R(x)#x
#define T(x)R(x)
#define S(p)b[9];main(i,v)char**v;{for(i=atoi(v[1]);i--;sprintf(b,"%i",i),mkdir(b),chdir(b));fputs("#define R(x)#x\n#define T(x)R(x)\n#define S(p)"p"\nS(T(S(p)))",fopen("d.c","w"));}
S(T(S(p)))

Here in a somewhat more readable form:

#define R(x) #x
#define T(x) R(x)
#define S(p) char b[9];\
             main(int i,char**v) { \
                for(i=atoi(v[1]); i--; sprintf(b,"%i",i), \
                                       mkdir(b), \
                                       chdir(b)); \
                fputs("#define R(x) #x\n" \
                      "#define T(x) R(x)\n" \
                      "#define S(p) " p "\n" \
                      "S(T(S(p)))", \
                      fopen("d.c", "w")); \
             }
S(T(S(p)))

The check if it works:

$ gcc -o d d.c
# a lot of warning and notes from gcc ... 
$ ./d 10
$ diff -s d.c 9/8/7/6/5/4/3/2/1/0/d.c
Files d.c and 9/8/7/6/5/4/3/2/1/0/d.c are identical

There most probably is a lot of golfing potential in the source code.

\$\endgroup\$
1
  • \$\begingroup\$ Great use of the preprocessor! \$\endgroup\$
    – LeFauve
    Jan 5, 2015 at 13:08
5
\$\begingroup\$

Batch - 48 * 0.9 = 43.2

for /l %%a in (1,1,%1)do md %%a&cd %%a&move..\%0

This script simply creates a new directory, and moves the source file to it - n times.

H:\MyDocuments\uprof\top>embed.bat 5

     ...

H:\MyDocuments\uprof\top>tree /f
Folder PATH listing for volume DATA009_HOMES
Volume serial number is B88B-384C
H:.
└───1
    └───2
        └───3
            └───4
                └───5
                        embed.bat
\$\endgroup\$
5
\$\begingroup\$

Zsh, 63 60 58 52 * 0.9 = 56.7 54 52.2 46.8

s=$(<$0);for i in {1..$1};{mkdir $i;cd $i};echo $s>f

Example:

llama@llama:...Code/misc/foo$ zsh f 5
llama@llama:...Code/misc/foo$ ls -R
.:
d1  f

./d1:
d2

./d1/d2:
d3

./d1/d2/d3:
d4

./d1/d2/d3/d4:
d5

./d1/d2/d3/d4/d5:
f
llama@llama:...Code/misc/foo$ cat d1/d2/d3/d4/d5/f 
s=$(cat $0);for i in {1..$1};do;mkdir d$i;cd d$i;done;echo $s>f
llama@llama:...Code/misc/foo$ cat f
s=$(cat $0);for i in {1..$1};do;mkdir d$i;cd d$i;done;echo $s>f
llama@llama:...Code/misc/foo$ diff f d1/d2/d3/d4/d5/f
llama@llama:...Code/misc/foo$
\$\endgroup\$
5
  • \$\begingroup\$ UUOC s=$(<$0) (Just for the record, it fails for me with bash 4.3.11: “syntax error near unexpected token `;'”. But works fine with zsh 5.0.2) \$\endgroup\$
    – manatwork
    Jan 5, 2015 at 10:16
  • \$\begingroup\$ Could you save a character by removing the d before $i? \$\endgroup\$ Jan 5, 2015 at 19:37
  • \$\begingroup\$ @CanadianLuke Huh, I never knew you could have a directory named 1. Thanks \$\endgroup\$
    – Doorknob
    Jan 5, 2015 at 22:00
  • \$\begingroup\$ I think you should be able to use curly brackets: for i in {1..$1};{mkdir $i;cd $i};echo $s>f. \$\endgroup\$
    – Ry-
    Jan 6, 2015 at 10:18
  • \$\begingroup\$ @U2744SNOWFLAKE Thanks, that saved a few bytes. Edited. \$\endgroup\$
    – Doorknob
    Jan 6, 2015 at 21:49
3
\$\begingroup\$

Rebol - 114 * 0.9 * 0.5 = 51.3

do b:[d: copy %./ repeat n do input[mkdir repend d[n"/"]]write join d s: system/options/script join"do b: "mold b]

Ungolfed:

do b: [
    d: copy %./
    repeat n do input [
        mkdir repend d [n "/"]
    ]
    write join d s: system/options/script join "do b: " mold b
]


Original non-quine version - 90 * 0.9 = 81

d: %./ repeat n do input[mkdir repend d[n"/"]write join d s: system/options/script read s]

Ungolfed:

d: %./
repeat n do input [
    mkdir repend d [n "/"]
]
write join d s: system/options/script read s
\$\endgroup\$
2
\$\begingroup\$

Bash 167*0.5*0.9 = 75.15

Borrowing heavily from @es1024's great answer, but this one is a true quine, so it qualifies for both bonuses.

b=\' c=\\ a='d=`seq -s/ $1`;mkdir -p $d;echo b=$c$b c=$c$c a=$b$a$b>>$d/$0;echo $a>>$d/$0'
d=`seq -s/ $1`;mkdir -p $d;echo b=$c$b c=$c$c a=$b$a$b>>$d/$0;echo $a>>$d/$0

Also, shell quine techniques from here.

\$\endgroup\$
1
\$\begingroup\$

AutoIt3, 106 * 0,9 = 95,4 bytes


A bit long but I can't help with those long function/variable names:

$f = @WorkingDir
For $i = 1 To $CmdLine[1]
    $f &= "\" & $i
Next
DirCreate($f)
FileCopy(@ScriptFullPath, $f)

Simply call it with <script/.exe name> <depth> eg. script.exe 5.
It will work for any amount of directories; maybe even more than your file system can handle. :D

How it works:

It's just a simple loop that adds the index to a string. Then the directory (and all parent directories, too) get created and the file copies itself to that directory.

\$\endgroup\$
1
\$\begingroup\$

Node.js, 136 133 * 0.9 * 0.5 = 61.2 59.85

r=require,f=r('fs'),p=__dirname;while(i=process.argv[2]--)f.mkdirSync(p+='/'+i);f.linkSync(a=__filename,p+'/'+r('path').basename(a))

fs.linkSync maps to the POSIX call link, which creates a hard link. An invalid argument will cause the program to crash.

\$\endgroup\$
1
\$\begingroup\$

J, 82 * 0.9 = 73.8

This is mostly a port of the top-voted answer.

exit (1!:1[1{A) 1!:2 <] (s,'/',>1{A)[fpathcreate s=:' /'charsub":1+i.".>{:A=:ARGV

Save as skittish.ijs or whatever you want, and call it from the command line using your version of jconsole. Mine is symlinked to jc:

$ jc skittish.ijs 20
$ ls 1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/skittish.ijs 
1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/skittish.ijs
\$\endgroup\$
0
\$\begingroup\$

Zsh, 55 * 0.9 * 0.5 = 24.75 bytes

I had my eyes on this challenge for a long time, but I wanted to complete it in Zsh without calling any external programs like mkdir and ln (otherwise, it would be identical to the bash solution). Turns out, Zsh can provide its own versions of those programs!

zmodload zsh/files
mkdir -p ${(j:/:):-{1..$1}}
ln $0 $_

Try it online!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.