This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kangaroo2K1's profile picture

Search engine across multiple disk drives?

Started by Kangaroo2K1, 27 April 2015 - 03:36 AM
Kangaroo2K1 #1
Posted 27 April 2015 - 05:36 AM
Hey, I'm fairly new to CC. I'd like to make a program where you enter a keyword into the program (running, not editing) and it searches across multiple disk drives in your wired network, and shows the results. Basically I want to make a search engine for my multi-drive network. I don't really know what to do, so all help is appreciated. Thanks! :)/>
Bomb Bloke #2
Posted 27 April 2015 - 06:40 AM
You'll first want to become familiar with looping, and with tables.

The way I'd approach this would be to first take the result of fs.list("") (which returns a numerically indexed table containing the names of all the files and folders in the root of the drive), then start a loop that inspects every element in that table, removing them as it goes. When it encounters a folder, it'd fs.list() its contents and add the contents of the new table to the first. When it encounters a match for the search term, it'd add that to a third table, which is what it'd return when the first table finally emptied.
Kangaroo2K1 #3
Posted 27 April 2015 - 11:38 PM
You'll first want to become familiar with looping, and with tables. The way I'd approach this would be to first take the result of fs.list("") (which returns a numerically indexed table containing the names of all the files and folders in the root of the drive), then start a loop that inspects every element in that table, removing them as it goes. When it encounters a folder, it'd fs.list() its contents and add the contents of the new table to the first. When it encounters a match for the search term, it'd add that to a third table, which is what it'd return when the first table finally emptied.
Thanks for the reply. I've been trying to code this for about 4 hours now, but still getting stumped overall. Could you (or anyone else) write this so that when you run it from the computer's directory (computer's HDD) it shows the results for all drives on the network and the computer? Thanks so much!
Edited on 27 April 2015 - 11:30 PM
Lyqyd #4
Posted 28 April 2015 - 01:33 AM
We don't generally write code on request. Post what you've got so far and explain what you're stuck on and we can help.
Kangaroo2K1 #5
Posted 28 April 2015 - 03:30 AM
Alright, I am very bad at coding (really sorry) but here's what I got: print(fs.list"") but all I get is a value (do I not print it?), nothing shown as a result. Any way to make it show all files, and then what would I enter to have it allow me to enter a search box to refine all files, and then what would I enter to actually single out results AND for it to show me what disk its on? Again, really sorry I'm bad at this. Thanks though for the help!
Creator #6
Posted 28 April 2015 - 07:22 AM
well, it works like this:


for i,v in pairs(fs.list(path)) do
print(v)
end
flaghacker #7
Posted 28 April 2015 - 11:37 AM
well, it works like this:


for i,v in pairs(fs.list(path)) do
print(v)
end

That only prints the files and folders in the root folder of the current computer, not the disk drives nor the contents of folders.
Kangaroo2K1 #8
Posted 28 April 2015 - 12:42 PM
well, it works like this:


for i,v in pairs(fs.list(path)) do
print(v)
end

That only prints the files and folders in the root folder of the current computer, not the disk drives nor the contents of folders.
Alright, how would I code it to see over all drives from being run in the computer? And what would I do next? Thanks.
DannySMc #9
Posted 28 April 2015 - 12:52 PM
Alright, how would I code it to see over all drives from being run in the computer? And what would I do next? Thanks.

Did you just want the names of the files or the paths too?

I have a script I use in my xPlore file manager (It will take some time to search through lots of items)
Edited on 28 April 2015 - 10:52 AM
DannySMc #10
Posted 28 April 2015 - 12:58 PM
Use this:

function searchall()
    local function yieldFsList(startPath)
        local list = fs.list(startPath)
        for _, file in ipairs(list) do
      	    local path = fs.combine(startPath, file)
       	    if fs.isDir(path) then
       	        yieldFsList(path)
            else
                coroutine.yield(path, file)
            end
        end
    end
    return coroutine.wrap(function() yieldFsList(start or "/") end
end

results = {}
for path, name in searchall() do
    table.insert(results[1], name)
    table.insert(results[2], path)
    sleep(0)
end

local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()

Try this, and it should generate a file for ALL the files in all disk drives…

Depending on speeds my emulator runs at 1000fps and searches the files at about 40 per second, even when taking out sleep otherwise it may error…
Edited on 28 April 2015 - 10:59 AM
Creator #11
Posted 28 April 2015 - 01:21 PM
yes, this is what it should be. I was just correcting the fs.list trouble he had ;)/>
Kangaroo2K1 #12
Posted 28 April 2015 - 09:31 PM
Use this:

function searchall()
	local function yieldFsList(startPath)
		local list = fs.list(startPath)
		for _, file in ipairs(list) do
			  local path = fs.combine(startPath, file)
	   		if fs.isDir(path) then
	   			yieldFsList(path)
			else
				coroutine.yield(path, file)
			end
		end
	end
	return coroutine.wrap(function() yieldFsList(start or "/") end
end

results = {}
for path, name in searchall() do
	table.insert(results[1], name)
	table.insert(results[2], path)
	sleep(0)
end

local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()

Try this, and it should generate a file for ALL the files in all disk drives…

Depending on speeds my emulator runs at 1000fps and searches the files at about 40 per second, even when taking out sleep otherwise it may error…
I put your code into the file I was trying to work with, saved it (entered code using Notepad, so there was no other formatting) and loaded into my game. After returning to my network pc I attempted to run the program, but it said in line 13 you were missing an end parenthesis. I checked to see, but it seemed to look correct. Frustrated, I deleted that file using the computer, and retyped your entire code. After another attempt, is said it was expecting <name> after search (program name I used). I had tried various combinations but it didn't seem to work. EX: search <mydoc1>, search<mydoc1>, search mydoc1, etc… What am I doing wrong here? Thanks for the awesome code, help is appreciated!
Dragon53535 #13
Posted 28 April 2015 - 09:49 PM
You need to edit a couple things. i'll fix that code for you.

local tArgs = {...} --# Need to grab your extra word
function searchall(start) --#Need to have it passed here so that you can choose
	    local function yieldFsList(startPath)
			    local list = fs.list(startPath)
			    for _, file in ipairs(list) do
						  local path = fs.combine(startPath, file)
					    if fs.isDir(path) then
							    yieldFsList(path)
					    else
							    coroutine.yield(path, file)
					    end
			    end
	    end
	    return coroutine.wrap(function() yieldFsList(start or "/") end) --# Someone forgot a ) here.
end
results = {}
for path, name in searchall(tArgs[1]) do --#Sending over your path
	    table.insert(results[1], name)
	    table.insert(results[2], path)
	    sleep(0)
end
local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()
That's about all I see being wrong
Kangaroo2K1 #14
Posted 28 April 2015 - 10:54 PM
You need to edit a couple things. i'll fix that code for you.

local tArgs = {...} --# Need to grab your extra word
function searchall(start) --#Need to have it passed here so that you can choose
		local function yieldFsList(startPath)
				local list = fs.list(startPath)
				for _, file in ipairs(list) do
						  local path = fs.combine(startPath, file)
						if fs.isDir(path) then
								yieldFsList(path)
						else
								coroutine.yield(path, file)
						end
				end
		end
		return coroutine.wrap(function() yieldFsList(start or "/") end) --# Someone forgot a ) here.
end
results = {}
for path, name in searchall(tArgs[1]) do --#Sending over your path
		table.insert(results[1], name)
		table.insert(results[2], path)
		sleep(0)
end
local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()
That's about all I see being wrong
Doesn't seem to work, say's table excepted, got nil even though the file is in one of the drives within my network. If someone does fix this, could they also post the command so I know I'm doing this right? Thanks again! :)/>
Edited on 28 April 2015 - 08:54 PM
Square789 #15
Posted 29 April 2015 - 02:24 PM
The code is trying to insert the name variable to the key value 1 of {results}, which doesn't exist because the table results is {}
Remove the "[1]" and "[2]" and it should works.

But you could do this:

table.insert(results, 1, name)
table.insert(results, 2, name)
Edited on 29 April 2015 - 12:26 PM
DannySMc #16
Posted 29 April 2015 - 02:55 PM
The code is trying to insert the name variable to the key value 1 of {results}, which doesn't exist because the table results is {}
Remove the "[1]" and "[2]" and it should works.

But you could do this:

table.insert(results, 1, name)
table.insert(results, 2, name)

Yeah he is using the file manager I made which contains the search function as I took it from that.
Dragon53535 #17
Posted 30 April 2015 - 12:48 AM
Square is correct, the indexes at 1 and 2 are not initialized to tables.


local tArgs = {...} --# Need to grab your extra word
function searchall(start) --#Need to have it passed here so that you can choose
		local function yieldFsList(startPath)
				local list = fs.list(startPath)
				for _, file in ipairs(list) do
						  local path = fs.combine(startPath, file)
						if fs.isDir(path) then
								yieldFsList(path)
						else
								coroutine.yield(path, file)
						end
				end
		end
		return coroutine.wrap(function() yieldFsList(start or "/") end) --# Someone forgot a ) here.
end
local results = {}
results[1] = {}
results[2] = {}
for path, name in searchall(tArgs[1]) do --#Sending over your path
		table.insert(results[1], name)
		table.insert(results[2], path)
		sleep(0)
end
local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()

There we go, I fixed it again
Kangaroo2K1 #18
Posted 30 April 2015 - 09:27 PM
Square is correct, the indexes at 1 and 2 are not initialized to tables.


local tArgs = {...} --# Need to grab your extra word
function searchall(start) --#Need to have it passed here so that you can choose
		local function yieldFsList(startPath)
				local list = fs.list(startPath)
				for _, file in ipairs(list) do
						  local path = fs.combine(startPath, file)
						if fs.isDir(path) then
								yieldFsList(path)
						else
								coroutine.yield(path, file)
						end
				end
		end
		return coroutine.wrap(function() yieldFsList(start or "/") end) --# Someone forgot a ) here.
end
local results = {}
results[1] = {}
results[2] = {}
for path, name in searchall(tArgs[1]) do --#Sending over your path
		table.insert(results[1], name)
		table.insert(results[2], path)
		sleep(0)
end
local output = fs.open("searchresults", "w")
output.write(textutils.serialize(results))
output.close()

There we go, I fixed it again
Even though I have a solution to this problem, I'd still like to have this code. This time during the first run the computer hung. After rebooting I did the command and then a keyword, it then said that my keyword was not a directory. How am I supposed to search across multiple drives if I don't know what directory I need to find it in?
Dragon53535 #19
Posted 01 May 2015 - 01:37 AM
Even though I have a solution to this problem, I'd still like to have this code. This time during the first run the computer hung. After rebooting I did the command and then a keyword, it then said that my keyword was not a directory. How am I supposed to search across multiple drives if I don't know what directory I need to find it in?
Another oversight, since the code will have to run over multiple drives, they'll each have a directory unique to them. However it also occurs to me that you're just looking for a specific file. Let me rewrite that code in it's entirety so that you can look for a specific file, without needing to know it's directory.

I'll edit this post when I finish it. Shouldn't be long.



local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
	for a,v in pairs(directory) do
	  if fs.isDir(v) then
		local fnd = recurFile(fs.combine(directory,v),searchTerm)
		if fnd ~= nil then
		  return fnd
		end
	  elseif v == searchTerm then
		return {true,fs.combine(directory,v)}
	  end
	end
  end
  for a,v in pairs(modem.getNamesRemote()) do
	if peripheral.getType(v) == "drive" then
	  local found = recurFile(disk.getMountPath(v),fileName)
	  if found ~= nil then
		return unpack(found)
	  end
	end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Make sure you edit that modem part to whatever side your wired modem is on
Edited on 30 April 2015 - 11:48 PM
Kangaroo2K1 #20
Posted 01 May 2015 - 02:16 AM
Even though I have a solution to this problem, I'd still like to have this code. This time during the first run the computer hung. After rebooting I did the command and then a keyword, it then said that my keyword was not a directory. How am I supposed to search across multiple drives if I don't know what directory I need to find it in?
Another oversight, since the code will have to run over multiple drives, they'll each have a directory unique to them. However it also occurs to me that you're just looking for a specific file. Let me rewrite that code in it's entirety so that you can look for a specific file, without needing to know it's directory.

I'll edit this post when I finish it. Shouldn't be long.



local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
	for a,v in pairs(directory) do
	  if fs.isDir(v) then
		local fnd = recurFile(fs.combine(directory,v),searchTerm)
		if fnd ~= nil then
		  return fnd
		end
	  elseif v == searchTerm then
		return {true,fs.combine(directory,v)}
	  end
	end
  end
  for a,v in pairs(modem.getNamesRemote()) do
	if peripheral.getType(v) == "drive" then
	  local found = recurFile(disk.getMountPath(v),fileName)
	  if found ~= nil then
		return unpack(found)
	  end
	end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Make sure you edit that modem part to whatever side your wired modem is on
Ended up with bad argument: table expected, got string. Doesn't seem to like how I ended up doing the command (search alongtime ago; one of the files across my network). And I did wrap the correct wired modem to the correct side.
Dragon53535 #21
Posted 01 May 2015 - 04:34 AM
Small oversight on my spot, didn't do it all correctly.

local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
	for a,v in pairs(fs.list(directory)) do --# Error was here, forgot fs.list
	  if fs.isDir(v) then
		local fnd = recurFile(fs.combine(directory,v),searchTerm)
		if fnd ~= nil then
		  return fnd
		end
	  elseif v == searchTerm then
		return {true,fs.combine(directory,v)}
	  end
	end
  end
  for a,v in pairs(modem.getNamesRemote()) do
	if peripheral.getType(v) == "drive" then
	  local found = recurFile(disk.getMountPath(v),fileName)
	  if found ~= nil then
		return unpack(found)
	  end
	end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Kangaroo2K1 #22
Posted 02 May 2015 - 01:56 PM
Small oversight on my spot, didn't do it all correctly.

local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
	for a,v in pairs(fs.list(directory)) do --# Error was here, forgot fs.list
	  if fs.isDir(v) then
		local fnd = recurFile(fs.combine(directory,v),searchTerm)
		if fnd ~= nil then
		  return fnd
		end
	  elseif v == searchTerm then
		return {true,fs.combine(directory,v)}
	  end
	end
  end
  for a,v in pairs(modem.getNamesRemote()) do
	if peripheral.getType(v) == "drive" then
	  local found = recurFile(disk.getMountPath(v),fileName)
	  if found ~= nil then
		return unpack(found)
	  end
	end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Attempted to search, but got this instead: "attempt to index ? (a nil value)"
Dragon53535 #23
Posted 02 May 2015 - 03:34 PM
Just to make sure, you're calling the program with an extra word right?
Kangaroo2K1 #24
Posted 02 May 2015 - 05:43 PM
Just to make sure, you're calling the program with an extra word right?
Yes, I type the command (search) and then the keyword (gameoflife, on one of my drives). So I enter search gameoflife. I've also tried searching for other keywords, but it doesn't work.
Dragon53535 #25
Posted 02 May 2015 - 06:13 PM
And the modem is on the right?
Kangaroo2K1 #26
Posted 02 May 2015 - 07:52 PM
And the modem is on the right?
Yes. If a red ring means on then yes, it is on.
flaghacker #27
Posted 02 May 2015 - 09:16 PM
Please post the full error, uncluding the line number.
Kangaroo2K1 #28
Posted 03 May 2015 - 03:27 AM
Please post the full error, uncluding the line number.
Played around with the code, ended up with a different result, what am I doing wrong here? https://www.dropbox.com/s/wubbzrcrwxqck1l/snip3.JPG?dl=0
Edited on 03 May 2015 - 01:27 AM
Dragon53535 #29
Posted 03 May 2015 - 11:57 PM
Ugh, it's not your fault. Silly getMountPath returns nil if you don't have a floppy in it. Give me a minute to fix it again with the correct line.

local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
	    for a,v in pairs(fs.list(directory)) do --# Error was here, forgot fs.list
		  if fs.isDir(v) then
			    local fnd = recurFile(fs.combine(directory,v),searchTerm)
			    if fnd ~= nil then
				  return fnd
			    end
		  elseif v == searchTerm then
			    return {true,fs.combine(directory,v)}
		  end
	    end
  end
  for a,v in pairs(modem.getNamesRemote()) do
	    if peripheral.getType(v) == "drive" and disk.hasData(v) then --# this should fix the problem
		  local found = recurFile(disk.getMountPath(v),fileName)
		  if found ~= nil then
			    return unpack(found)
		  end
	    end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Edited on 03 May 2015 - 09:58 PM
Kangaroo2K1 #30
Posted 04 May 2015 - 04:01 AM
Ugh, it's not your fault. Silly getMountPath returns nil if you don't have a floppy in it. Give me a minute to fix it again with the correct line.

local tArgs = {...}
local modem = peripheral.wrap("right")
local function findFile(fileName)
  local function recurFile(directory,searchTerm)
		for a,v in pairs(fs.list(directory)) do --# Error was here, forgot fs.list
		  if fs.isDir(v) then
				local fnd = recurFile(fs.combine(directory,v),searchTerm)
				if fnd ~= nil then
				  return fnd
				end
		  elseif v == searchTerm then
				return {true,fs.combine(directory,v)}
		  end
		end
  end
  for a,v in pairs(modem.getNamesRemote()) do
		if peripheral.getType(v) == "drive" and disk.hasData(v) then --# this should fix the problem
		  local found = recurFile(disk.getMountPath(v),fileName)
		  if found ~= nil then
				return unpack(found)
		  end
		end
  end
end
local found,path = findFile(tArgs[1])
if found then print(path) end
Success! Thanks to all who helped with this, I do appreciate it. If need be, I could end up helping you, just not with coding (Building something cool). But anyway, thanks again! https://www.dropbox.com/s/yxzb3jxdncir435/snip4.JPG?dl=0