A Skittish Program

17

1

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

globby

Posted 2015-01-05T00:01:08.993

Reputation: 1 132

1What counts as "reading the source"? You mean the file? Or the real source code? – GiantTree – 2015-01-05T00:04:00.753

The file. Will clarify in post @GiantTree – globby – 2015-01-05T00:04:30.093

@globby What do you mean by 'do not read the source file of the program'? – unclemeat – 2015-01-05T01:47:56.933

Copying the exact source into the directory, without explicitly reading the file that your source code is in. Basically make a quine that drops itself into another directory. Sorry if I'm not explaining it well – globby – 2015-01-05T01:56:40.127

@globby So execution of the CMD command move does not count as reading the source file? – unclemeat – 2015-01-05T02:04:11.497

2@unclemeat It does, because to move the file you (or the system) has to access the data in the file. – globby – 2015-01-05T02:05:57.183

25Seems like you are just trying to hide your porn stash. – Ablue – 2015-01-05T03:57:36.137

1I'm not paying enough attention to tell; is the [quine] tag appropriate? – Justin – 2015-01-05T07:13:00.973

3@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. – h.j.k. – 2015-01-05T08:18:33.720

Seriously don't need to write a quine this late... – Isiah Meadows – 2015-01-05T08:33:18.900

7@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. – Martin Ender – 2015-01-05T09:10:25.030

@MartinBüttner fair. Updated the original post. – globby – 2015-01-05T15:36:40.750

Unique names up to depth 1 000 000? If I see code which promises to do that, but looks like the cat danced on the keyboard, I would never dare test it on my computer. Not with an input above 10, anyway. – rumtscho – 2015-01-05T17:57:52.263

The logic just needs to work up to depth 1000000. You can modify the code to print the directory names instead of making them, or work out the logic on paper. – globby – 2015-01-05T18:03:31.957

A million nested subfolders? Hey man, some of us use Windows! – user1354557 – 2015-01-07T22:06:47.700

@MarkReed Look a few comments up – globby – 2015-01-08T15:43:20.697

Answers

36

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.

es1024

Posted 2015-01-05T00:01:08.993

Reputation: 8 953

6Brilliant use of $_! – wchargin – 2015-01-05T03:04:29.070

2

Save a char - use backticks instead of $( ): http://codegolf.stackexchange.com/a/25572/11259

– Digital Trauma – 2015-01-05T05:32:58.077

2

Depending on the answer to my comment on the question, maybe replace cp with ln to get the 0.5 bonus too...

– h.j.k. – 2015-01-05T08:20:22.120

2Wells, 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? – h.j.k. – 2015-01-05T17:38:40.397

2Yes, 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! – Tobia – 2015-01-05T22:45:43.023

12

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.

MarcDefiant

Posted 2015-01-05T00:01:08.993

Reputation: 996

Great use of the preprocessor! – LeFauve – 2015-01-05T13:08:26.787

5

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$

Doorknob

Posted 2015-01-05T00:01:08.993

Reputation: 68 138

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) – manatwork – 2015-01-05T10:16:02.163

Could you save a character by removing the d before $i? – Canadian Luke – 2015-01-05T19:37:31.270

@CanadianLuke Huh, I never knew you could have a directory named 1. Thanks – Doorknob – 2015-01-05T22:00:37.530

I think you should be able to use curly brackets: for i in {1..$1};{mkdir $i;cd $i};echo $s>f. – Ry- – 2015-01-06T10:18:49.003

@U2744SNOWFLAKE Thanks, that saved a few bytes. Edited. – Doorknob – 2015-01-06T21:49:22.907

5

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

unclemeat

Posted 2015-01-05T00:01:08.993

Reputation: 2 302

3

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

draegtun

Posted 2015-01-05T00:01:08.993

Reputation: 1 592

2

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.

Digital Trauma

Posted 2015-01-05T00:01:08.993

Reputation: 64 644

1

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.

GiantTree

Posted 2015-01-05T00:01:08.993

Reputation: 885

1

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.

c.P.u1

Posted 2015-01-05T00:01:08.993

Reputation: 1 049

1

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

hoosierEE

Posted 2015-01-05T00:01:08.993

Reputation: 760

0

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!

GammaFunction

Posted 2015-01-05T00:01:08.993

Reputation: 2 838