diff --git a/installer/nixos-hardware-scan.pl b/installer/nixos-hardware-scan.pl
new file mode 100644
index 000000000000..5b2f6692d865
--- /dev/null
+++ b/installer/nixos-hardware-scan.pl
@@ -0,0 +1,174 @@
+#! @perl@ -w
+
+use File::Spec;
+use File::Basename;
+
+
+my @kernelModules = ();
+my @initrdKernelModules = ();
+
+
+# Read a file, returning undef if the file cannot be opened.
+sub readFile {
+    my $filename = shift;
+    my $res;
+    if (open FILE, "<$filename") {
+        my $prev = $/;
+        undef $/;
+        $res = <FILE>;
+        $/ = $prev;
+        close FILE;
+        chomp $res;
+    }
+    return $res;
+}
+
+
+my $cpuinfo = readFile "/proc/cpuinfo";
+
+
+sub hasCPUFeature {
+    my $feature = shift;
+    return $cpuinfo =~ /^flags\s*:.* $feature( |$)/m;
+}
+
+
+# Detect the number of CPU cores.
+my $cpus = scalar (grep {/^processor\s*:/} (split '\n', $cpuinfo));
+
+
+# CPU frequency scaling.  Not sure about this test.
+push @kernelModules, "acpi-cpufreq" if hasCPUFeature "acpi";
+
+
+# Virtualization support?
+push @kernelModules, "kvm-intel" if hasCPUFeature "vmx";
+push @kernelModules, "kvm-amd" if hasCPUFeature "svm";
+
+
+# Look at the PCI devices and add necessary modules.  Note that most
+# modules are auto-detected so we don't need to list them here.
+# However, some are needed in the initrd to boot the system.
+
+sub pciCheck {
+    my $path = shift;
+    my $vendor = readFile "$path/vendor";
+    my $device = readFile "$path/device";
+    my $class = readFile "$path/class";
+    
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+    
+    print "$path: $vendor $device $class";
+    print " $module" if defined $module;
+    print "\n";
+
+    if (defined $module) {
+        # See the bottom of http://pciids.sourceforge.net/pci.ids for
+        # device classes.
+        if (# Mass-storage controller.  Definitely important.
+            $class =~ /^0x01/ ||
+
+            # Firewire controller.  A disk might be attached.
+            $class =~ /^0x0c00/ ||
+
+            # USB controller.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            $class =~ /^0x0c03/
+            )
+        {
+            push @initrdKernelModules, $module;
+        }
+    }        
+}
+
+foreach my $path (glob "/sys/bus/pci/devices/*") {
+    pciCheck $path;
+}
+
+
+# Idem for USB devices.
+
+sub usbCheck {
+    my $path = shift;
+    my $class = readFile "$path/bInterfaceClass";
+    my $subclass = readFile "$path/bInterfaceSubClass";
+    my $protocol = readFile "$path/bInterfaceProtocol";
+
+    my $module;
+    if (-e "$path/driver/module") {
+        $module = basename `readlink -f $path/driver/module`;
+        chomp $module;
+    }
+    
+    print "$path: $class $subclass $protocol";
+    print " $module" if defined $module;
+    print "\n";
+ 
+    if (defined $module) {
+        if (# Mass-storage controller.  Definitely important.
+            $class eq "08" ||
+
+            # Keyboard.  Needed if we want to use the
+            # keyboard when things go wrong in the initrd.
+            ($class eq "03" && $protocol eq "01")
+            )
+        {
+            push @initrdKernelModules, $module;
+        }
+    }
+}
+
+foreach my $path (glob "/sys/bus/usb/devices/*") {
+    if (-e "$path/bInterfaceClass") {
+        usbCheck $path;
+    }
+}
+
+
+
+# Generate the configuration file.
+
+sub removeDups {
+    my %seen;
+    my @res = ();
+    foreach my $s (@_) {
+        if (!defined $seen{$s}) {
+            $seen{$s} = "";
+            push @res, $s;
+        }
+    }
+    return @res;
+}
+
+sub toNixExpr {
+    my $res = "";
+    foreach my $s (@_) {
+        $res .= " \"$s\"";
+    }
+    return $res;
+}
+
+my $initrdKernelModules = toNixExpr(removeDups @initrdKernelModules);
+my $kernelModules = toNixExpr(removeDups @kernelModules);
+ 
+
+print <<EOF ;
+# This is a generated file.  Do not modify!
+# Make changes to /etc/nixos/configuration.nix instead.
+{
+  boot = {
+    initrd = {
+      extraKernelModules = [ $initrdKernelModules ];
+    };
+    kernelModules = [ $kernelModules ];
+  };
+
+  nix = {
+    maxJobs = $cpus;
+  };
+}
+EOF
diff --git a/installer/nixos-hardware-scan.sh b/installer/nixos-hardware-scan.sh
deleted file mode 100644
index 06fbb11dfa1f..000000000000
--- a/installer/nixos-hardware-scan.sh
+++ /dev/null
@@ -1,144 +0,0 @@
-#! @shell@ -e
-
-
-kernelModules=
-initrdKernelModules=
-
-hasCPUFeature() {
-    local feature="$1"
-    cat /proc/cpuinfo | grep -q "^flags.* $feature\( \|$\)"
-}
-
-needKernelModule() {
-    kernelModules="$kernelModules \"$1\""
-}
-
-needInitrdKernelModule() {
-    initrdKernelModules="$initrdKernelModules \"$1\""
-}
-
-
-# Detect the number of CPU cores.
-cpus="$(cat /proc/cpuinfo | grep '^processor' | wc -l)"
-
-
-# CPU frequency scaling.  Not sure about this test.
-if hasCPUFeature "acpi"; then
-    needKernelModule "acpi-cpufreq"
-fi
-
-
-# Virtualization support?
-if hasCPUFeature "vmx"; then
-    needKernelModule "kvm-intel"
-fi
-
-if hasCPUFeature "svm"; then
-    needKernelModule "kvm-amd"
-fi
-
-
-# Look at the PCI devices and add necessary modules.  Note that most
-# modules are auto-detected so we don't need to list them here.
-# However, some are needed in the initrd to boot the system.
-
-pciCheck() {
-    local path="$1"
-    local vendor="$(cat $path/vendor)" 
-    local device="$(cat $path/device)"
-    local class="$(cat $path/class)"
-
-    local module
-    if test -e "$path/driver/module"; then
-        module=$(basename $(readlink -f $path/driver/module))
-    fi
-
-    echo "$path: $vendor $device $class $module"
-
-    if test -n "$module"; then
-        # See the bottom of http://pciids.sourceforge.net/pci.ids for
-        # device classes.
-        case $class in
-            0x01*)
-                # Mass-storage controller.  Definitely important.
-                needInitrdKernelModule $module
-                ;;
-            0x0c00*)
-                # Firewire controller.  A disk might be attached.
-                needInitrdKernelModule $module
-                ;;
-            0x0c03*)
-                # USB controller.  Needed if we want to use the
-                # keyboard when things go wrong in the initrd.
-                needInitrdKernelModule $module
-                ;;
-        esac
-    fi        
-}
-
-for path in /sys/bus/pci/devices/*; do
-    pciCheck "$path"
-done
-
-
-# Idem for USB devices.
-
-usbCheck() {
-    local path="$1"
-    local class="$(cat $path/bInterfaceClass)" 
-    local subclass="$(cat $path/bInterfaceSubClass)"
-    local protocol="$(cat $path/bInterfaceProtocol)"
-
-    local module
-    if test -e "$path/driver/module"; then
-        module=$(basename $(readlink -f $path/driver/module))
-    fi
-
-    echo "$path: $class $subclass $protocol $module"
-
-    if test -n "$module"; then
-        # See http://www.linux-usb.org/usb.ids for
-        # classes/subclasses/protocols.
-        case $class:$subclass:$protocol in
-            08:*)
-                # Mass-storage controller.  Definitely important.
-                needInitrdKernelModule $module
-                ;;
-            03:*:01)
-                # Keyboard.  Needed if we want to use the
-                # keyboard when things go wrong in the initrd.
-                needInitrdKernelModule $module
-                ;;
-        esac
-    fi        
-}
-
-for path in /sys/bus/usb/devices/*; do
-    if test -e "$path/bInterfaceClass"; then
-        usbCheck "$path"
-    fi
-done
-
-
-# Remove duplicate modules.  !!! preserve order
-kernelModules=$(for i in $kernelModules; do echo $i; done | sort | uniq)
-initrdKernelModules=$(for i in $initrdKernelModules; do echo $i; done | sort | uniq)
-
-
-# Generation the configuration file.
-cat <<EOF
-# This is a generated file.  Do not modify!
-# Make changes to /etc/nixos/configuration.nix instead.
-{
-  boot = {
-    initrd = {
-      extraKernelModules = [ $(echo $initrdKernelModules) ];
-    };
-    kernelModules = [ $(echo $kernelModules) ];
-  };
-
-  nix = {
-    maxJobs = $cpus;
-  };
-}
-EOF